8.3 Usage
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 );
}
}
}
|