Flylib.com

Books Software

 
 
 

7.4 Test Assert Methods

     

7.4 Test Assert Methods

CppUnit provides several variations on the basic assert method. The assert methods are implemented as macros. The advantage of using macros to implement assert methods is that they enable the compiler preprocessor to record the source code location of each assert, which is otherwise hard to do in C.

As in other xUnits, some of the asserts have variants that take a descriptive message argument. The message is reported if the test fails. Examples of these variants are shown in the following list:


CPPUNIT_ASSERT(condition)

CPPUNIT_ASSERT_MESSAGE(message,condition)

Test that passes if condition is true .


CPPUNIT_FAIL(message)

Test that always fails.


CPPUNIT_ASSERT_EQUAL(expected,actual)

CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,actual)

Test that passes if expected and actual are equal. It supports arguments of most common data types and std::string .


CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,actual,delta)

Test that passes if expected and actual are equal within a tolerance of delta . The arguments are of type double .

     

Chapter 8. NUnit

Section 8.1.   Overview

Section 8.2.   Architecture

Section 8.3.   Usage

Section 8.4.   Test Assert Methods

     

8.1 Overview

NUnit is a unit test framework for the Microsoft .NET architecture. Conceptually, it follows the xUnit model, serving as a foundation for building unit test classes and methods . It is implemented in C#, but supports writing unit tests in any .NET language, including C#, J#, Managed C++, and Microsoft Visual Basic .NET (VB.NET). NUnit defines tests using C# attributes rather than object inheritance, so the details of its software architecture differ significantly from JUnit.

NUnit is open source software released under a public license. The license permits NUnit to be freely redistributed and altered , as long as the original copyright notice is included and any alterations are acknowledged . The copyright holders and main developers of NUnit are James Newkirk, Michael Two, Alexei Vorontsov, Philip Craig, and Charlie Poole.

For additional information refer to the NUnit web site, http://www.nunit.org. The summary in this chapter is based on Version 2.1.

     

8.2 Architecture

NUnit is a full-featured unit test framework built using TDD. The distribution includes unit tests covering all of NUnit's functionality. Aside from the core framework, NUnit also includes GUI and console test runners, code samples, extensions, and utilities.

NUnit relies on C# attributes to structure test code. In contrast to the conventional object-oriented definition of an attribute, a C# attribute is metadata attached to a code element such as a class or method. These attributes contain descriptive declarations that may be accessed at runtime. NUnit attributes such as Test and TestFixture allow the test framework to identify test methods and classes. This approach makes it possible to build unit tests with minimal knowledge of the underlying NUnit code structure.

     

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
figs/utf_0801.gif

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 );

      }

   }

}