Unit Test Frameworks

     

A test class is defined using the TestFixture attribute. Test methods are defined using the Test attribute. Example 8-1 shows a simple unit test for the class Book . The source code for the classes Book and Library is given at the end of this section.

Example 8-1. The test class BookTest

BookTest.cs using System; namespace LibraryTests { using Library; using NUnit.Framework; [ TestFixture ] public class BookTest { [ Test ] public void TestCreateBook ( ) { Book book = new Book( "Cosmos", "Carl Sagan" ); Assert.AreEqual( "Cosmos", book.title, "wrong title" ); Assert.AreEqual( "Carl Sagan", book.author, "wrong author" ); } } }

In this example, the test class BookTest is defined as a TestFixture , and the method TestCreateBook( ) is a Test . At runtime, all of the Test methods are found and run.

The attributes SetUp and TearDown are used to implement test fixture behavior. The SetUp method is called prior to each Test method, and the TearDown method is called afterwards. Example 8-2 shows a test for the class Library , implemented as a test fixture.

Example 8-2. The test class LibraryTest

LibraryTest.cs using System; namespace LibraryTests { using Library; using NUnit.Framework; [ TestFixture ] public class LibraryTest { private Library library; [ SetUp ] public void SetUp ( ) { library = new Library( ); library.addBook(new Book( "Cosmos", "Carl Sagan" )); library.addBook(new Book( "Contact", "Carl Sagan" )); } [ TearDown ] public void TearDown ( ) { } [ Test ] public void TestGetBookByTitleAndAuthor ( ) { Book book = library.getBook( "Cosmos", "Carl Sagan" ); Assert.AreEqual( "Cosmos", book.title, "wrong title" ); Assert.AreEqual( "Carl Sagan", book.author, "wrong author" ); } [ Test ] public void TestRemoveBook ( ) { library.removeBook( "Cosmos" ); Book book = library.getBook( "Cosmos", "Carl Sagan" ); Assert.IsNull( book, "book not removed" ); } } }

The SetUp( ) method creates a Library and adds two Book s to it. Since C# has automatic garbage collection, it is not necessary for the TearDown( ) method to deallocate the test objects.

Like other xUnits, NUnit provides numerous test assert methods. Examples Example 8-1 and Example 8-2 demonstrate usage of Assert.AreEqual() and Assert.IsNull() .

Methods identified by the attributes TestFixtureSetUp and TestFixtureTearDown act similarly to SetUp and TearDown , but are called only once for a given TestFixture rather than for each test method. This feature is useful when it is undesirable to initialize an object multiple times, such as when creating it is computationally intensive . However, TestFixtureSetUp and TestFixtureTearDown should be used with caution because they introduce the potential for test coupling. If multiple test methods share an object that may change state during the test, the tests are not isolated. Using TestFixtureSetUp is safe when the shared objects being initialized cannot be affected by the test methods. Example 8-3 shows how TestFixtureSetUp and TestFixtureTearDown could be used in LibraryTest .

Example 8-3. The test class LibraryTest, using TestFixtureSetUp

LibraryTest.cs [ TestFixtureSetUp ] public void TestFixtureSetUp ( ) { book1 = new Book( "Cosmos", "Carl Sagan" ); book2 = new Book( "Contact", "Carl Sagan" ); } [ TestFixtureTearDown ] public void TestFixtureTearDown ( ) { } [ SetUp ] public void SetUp ( ) { library = new Library( ); library.addBook(book1); library.addBook(book2); } [ TearDown ] public void TearDown ( ) { }

Since the Book objects won't be modified by the test methods, they can safely be created in TestFixtureSetUp( ) without risking test coupling. The test methods can change the state of the Library object, so it must be initialized in SetUp( ) to guarantee that it is in the same state for each test.

When LibraryTest is run, the sequence of calls is:

TestFixtureSetUp( ) SetUp( ) TestGetBookByTitleAndAuthor( ) TearDown( ) SetUp( ) TestRemoveBook( ) TearDown( ) TestFixtureTearDown( )

The TestFixtureSetUp() and TestFixtureTearDown( ) methods are called once for the test fixture, whereas SetUp( ) and TearDown( ) are called for each test method.

NUnit supports writing tests for expected error behavior using the ExpectedException attribute. Example 8-4 shows a LibraryTest test method to check that attempting to remove a nonexistent Book causes an Exception to be thrown.

Example 8-4. The test method TestRemoveNonexistentBook

LibraryTest.cs [ Test ] [ ExpectedException(typeof(Exception)) ] public void TestRemoveNonexistentBook ( ) { library.removeBook( "Nonexistent" ); }

The attribute ExpectedException(typeof(Exception)) indicates that the test method is expected to throw an Exception . If an Exception is not thrown, the test fails. The attribute Test still is necessary to indicate that this is a test method.

Another NUnit attribute is Ignore , which specifies that a test method should not be run. This can be useful for temporarily disabling a failing test. Example 8-5 demonstrates its usage.

Example 8-5. The test method TestBadTest

LibraryTest.cs [Test, Ignore("Bad test")] public void TestBadTest ( ) { Assert.Fail( "Always fails" ); }

Since the test method TestBadTest( ) has the Ignore attribute, NUnit will not run it. Attributes can be combined, as shown by the compound Test and Ignore attributes.

NUnit tests are run using the NUnit GUI or a console test runner. When using the GUI, the File Open menu command is used to open the . DLL or . EXE file containing the unit tests. The tests found are displayed in the GUI as a hierarchy of test fixtures and test methods. The Run button executes the tests and displays their results, as shown in Figure 8-1.

Figure 8-1. The NUnit GUI

Successful tests are flagged green and failures are red. Tests marked with the Ignore attribute are not run and are flagged with a yellow mark. The status bar on the right indicates the overall result: green if everything succeeds, red if there is a failure, and yellow if any tests are skipped .

Running tests using the console test runner is similar, as shown in Example 8-6.

Example 8-6. Running tests using the NUnit console

>nunit-console.exe C:\Work\Library.dll .....N Tests run: 4, Failures: 0, Not run: 1, Time: 0.050072 seconds Tests not run: LibraryTests.LibraryTest.TestBadTest : Bad test

Test failures and tests that are not run are reported , along with the total number of tests.

The C# class Book referred to in this chapter is shown in Example 8-7.

Example 8-7. The class Book

Book.cs using System; namespace Library { public class Book { public string title; public string author; public Book (string title, string author) { this.title = title; this.author = author; } } }

The class Library is shown in Example 8-8.

Example 8-8. The class Library

Library.cs using System; using System.Collections; namespace Library { public class Library { private Hashtable books; public Library ( ) { books = new Hashtable( ); } public void addBook (Book book) { books.Add( book.title, book ); } public Book getBook (string title, string author) { return (Book)books[ title ]; } public void removeBook (string title) { if ( books[ title ] == null ) throw new Exception( "book not found" ); books.Remove( title ); } } }

Категории