Other NUnit Capabilities


With our previous example, we introduced the basic NUnit features and capabilities. TestFixture , Test , and Assert are the three most fundamental features that you need to know to start writing programmer tests with NUnit. What we will describe now will build on these three features.

Using SetUp/TearDown Attributes

In the definition of a test fixture that we gave earlier, we said that tests in a test fixture share a common set of run-time resources. These common run-time resources may need to be acquired and initialized in a certain way before a test is executed and released or cleaned up after the test is completed. NUnit supports this common initialization/cleanup process using two additional custom attributes: SetUp and TearDown . Let s extend the original example to demonstrate this capability. Let s add multiplication:

 using System; using NUnit.Framework; namespace NUnitQuickStart {    [TestFixture]    public class NumbersFixture    {       [Test]       public void AddTwoNumbers()       {          int a = 1;          int b = 2;          int sum = a + b;          Assert.AreEqual(3, sum);       }       [Test]       public void MultiplyTwoNumbers()       {          int a = 1;          int b = 2;          int product = a * b;          Assert.AreEqual(2, product);       }    } } 

We can share the initialization code of the operands by extracting this code into a separate method and marking this method with a SetUp attribute (the local variables have to change their scope and become instance variables ). Here is the code after the change:

 using System; using NUnit.Framework; namespace NUnitQuickStart {    [TestFixture]    public class NumbersFixture    {       private int a;       private int b;       [SetUp]       void InitializeOperands()       {          a = 1;          b = 2;       }       [Test]       public void AddTwoNumbers()       {             int sum = a + b;          Assert.AreEqual(3, sum);       }       [Test]       public void MultiplyTwoNumbers()       {          int product = a * b;          Assert.AreEqual(2, product);       }    } } 

The NUnit test runner will execute the method marked with the SetUp attribute prior to each test.

Using ExpectedException

Now let s add division to the example. We want to make sure that dividing by zero works, as promised by the .NET documentation. Here is a test to verify this assumption.

 [Test] [ExpectedException(typeof(DivideByZeroException))] public void DivideByZero() {    int zero = 0;    int infinity = a/zero;    Assert.Fail("Should have gotten an exception"); } 

In addition to the Test attribute, the DivideByZero method has another custom attribute: ExpectedException . You can use this attribute to indicate that an exception of a particular type is expected during the execution of the test method. If the method completes without throwing the expected exception, the test fails. Using this attribute helps in writing programmer tests that verify boundary conditions.

Using the Ignore Attribute

Sometimes, we have tests that we don t want to run. There can be many reasons for this: you may be in the middle of a refactoring that is breaking a lot of tests and you can t stand the sight of the red bar; or you may have written a programmer test to capture some not-fully- understood requirements. Using the Ignore attribute, you can keep the tests but not execute them. Let s mark the MultiplyTwoNumbers test method with the Ignore attribute:

 [Test] [Ignore("Multiplication is ignored")] public void MultiplyTwoNumbers() {    int product = a * b;    Assert.AreEqual(2, product); } 

Running these tests now produces the following output (shown in Figure A-6):

click to expand
Figure A-6: Using the Ignore attribute with a programmer test

The Ignore attribute can be attached to either an individual test method or an entire test class ( TestFixture ). If the Ignore attribute is attached to the TestFixture , all the tests in the fixture will be ignored.

Using TestFixtureSetUp / TestFixtureTearDown

Sometimes, the resources needed by a set of tests are expensive to acquire. For example, database connections can be a critical resource, and opening/closing a database connection for every test in a test fixture may be too slow. NUnit has a pair of attributes that are similar to the SetUp / TearDown attributes discussed earlier: TestFixtureSetUp / TestFixtureTearDown . As their names suggest, these attributes are used to mark methods that initialize/release resources once for the entire test fixture.

For example, if we want to share the same database connection object for all the tests in our test fixture, we can write a method that opens a database connection and mark it with the TestFixtureSetUp attribute, another method that would close the database connection we would mark with the TestFixtureTearDown attribute. Here is an example that demonstrates this:

 using NUnit.Framework; [TestFixture] public class DatabaseFixture {    [TestFixtureSetUp]    public void OpenConnection()    {       //open the connection to the database    }    [TestFixtureTearDown]    public void CloseConnection()    {       //close the connection to the database    }        [SetUp]    public void CreateDatabaseObjects()    {       //insert the records into the database table    }    [TearDown]    public void DeleteDatabaseObjects()    {       //remove the inserted records from the database table    }    [Test]    public void ReadOneObject()    {       //load one record using the open database connection    }    [Test]    public void ReadManyObjects()    {       //load many records using the open database connection    } } 

Test Life-Cycle Contract

If you recall our definition of the test case, one of the properties is the test s independence or isolation. The pair of SetUp/TearDown methods serves the purpose of achieving test isolation. SetUp makes sure that the shared resources are correctly initialized before each test is run, and TearDown makes sure that there are no leftover side effects produced by running tests. The TestFixtureSetUp / TestFixtureTearDown attributes serve a similar purpose, but at the test fixture scope. What we just described constitutes the life-cycle contract between the test framework s run-time container (test runner) and the tests you write.

To demonstrate this contract, we write a simple test that shows what methods get called and when. Here s the code:

 using System; using NUnit.Framework; [TestFixture] public class LifeCycleContractFixture {    [TestFixtureSetUp]    public void FixtureSetUp()    {       Console.Out.WriteLine("FixtureSetUp");    }    [TestFixtureTearDown]    public void FixtureTearDown()    {       Console.Out.WriteLine("FixtureTearDown");    }        [SetUp]    public void SetUp()    {       Console.Out.WriteLine("SetUp");    }    [TearDown]    public void TearDown()    {       Console.Out.WriteLine("TearDown");    }    [Test]    public void Test1()    {       Console.Out.WriteLine("Test 1");    }    [Test]    public void Test2()    {       Console.Out.WriteLine("Test 2");    } } 

When you compile and run this test, you see the following output in the console System.Console window:

 FixtureSetUp SetUp Test 1 TearDown SetUp Test 2 TearDown FixtureTearDown 

As you can see, the SetUp / TearDown methods are called before and after each test method; the TestFixtureSetUp / TestFixtureTearDown methods are called once for the entire fixture.




Test-Driven Development in Microsoft .NET
Test-Driven Development in Microsoft .NET (Microsoft Professional)
ISBN: 0735619484
EAN: 2147483647
Year: 2004
Pages: 85

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net