| 前一章我们简单介绍了NUnit的入门示例《一步一步学NUnit(一)》,让大家对NUnit有个简单的认识。 
                          NUnit的使用是非常简单的,但是它在项目中使用时,有许多最佳实践。这章我们把上一章没有讲到的NUnit的一些配置和特性介绍一下。 要想熟练地使用NUnit还是要在实践中使用和体会,单纯地学习知识点是没有用的。 好,不再废话了。继续上一章的内容。 在Visual Studio 2008 中打开上一章的示例,Calculator类有4个最简单的方法:加、减、乘、除。CalculatorTest类中的四个方法是Calculator类四个方法的单元测试。 
                           
                            | [TestFixture] public class CalculatorTest
 ...{
 [Test]
 public void TestAdd()
 ...{
 Calculator 
                              cal = new Calculator();
 int expected = 5;
 int actual = cal.Add(2, 3);
 Assert.AreEqual(expected, actual);
 }
 [Test]
 public void TestMinus()
 ...{
 Calculator 
                              cal = new Calculator();
 int expected = 5;
 int actual = cal.Minus(10, 5);
 Assert.AreEqual(expected, actual);
 }
 [Test]
 public void TestMultiply()
 ...{
 Calculator cal = new Calculator();
 int expected = 5;
 int actual = cal.Multiply(1, 5);
 Assert.AreEqual(expected, actual);
 }
 [Test]
 public void TestDivide()
 ...{
 Calculator cal = new Calculator();
 int expected = 5;
 int actual = cal.Divide(25, 5);
 Assert.AreEqual(expected, actual);
 }
 }
 |  这里一定要注意,TestAdd()、TestMinus()、TestMultiply()和TestDivide()方法没有任何关系,也就是说单元测试中,所有的测试方法都是独立的。各个方法之间没有依赖性,删除任何一个单元测试方法,对其它的测试不会有任何影响。 上一章中,我们已经介绍了[TestFixture]和[Test],现在我们为这个类新增一个方法。 
                           
                            | [SetUp] public void InitMethod()
 {
 Console.WriteLine("Initialization 
                              method");
 }
 |  重新生成项目,再运行NUnit,选中"CalculatorTest"进行单元测试:   切换到NUnit的"Console.Out"中,我们看到"Initialization 
                          method"出现了4次,如果只选中一个测试方法:   我们看到,这时只出现一次的"Initialization method"。[SetUp]的意思就是指在运行每个测试方法前执行它。相应的,有开始必然有结束,[TearDown]是指在每个测试方法结束后运行。 
                         我们再新增一个方法:  
                           
                            | [TearDown] public void FinalizeMethod()
 {
 Console.WriteLine("Finalize 
                              method");
 }
 |  再来看运行NUnit的结果:   知道了[SetUp]和[TearDown]后,我们就可以改写这个单元测试类了。 
                           
                            | 请[TestFixture] public class CalculatorTest
 ...{
 private Calculator cal;
 private int a, b, expected, actual;
     [SetUp]public void InitMethod()
 ...{
 cal 
                                = new Calculator();
 a = 
                                10;
 b = 
                                2;
 }
     [Test]public void TestAdd()
 ...{
 expected 
                                = 12;
 actual 
                                = cal.Add(a, b);
 Assert.AreEqual(expected, 
                                actual);
 }
 [Test]
 public void TestMinus()
 ...{
 expected 
                                = 8;
 actual 
                                = cal.Minus(a, b);
 Assert.AreEqual(expected, 
                                actual);
 }
 [Test]
 public void TestMultiply()
 ...{
 expected 
                                = 20;
 actual 
                                = cal.Multiply(a, b);
 Assert.AreEqual(expected, 
                                actual);
 }
 [Test]
 public void TestDivide()
 ...{
 expected 
                                = 5;
 actual 
                                = cal.Divide(a, b);
 Assert.AreEqual(expected, 
                                actual);
 }
 }
 |  因为运行每个测试方法之前,都会运行InitMethod()方法,所以每次都会初始化使第一个操作数为10,第二个操作数为2。在[SetUp]中初始化了的资源,我们就可以在[TearDown]里销毁释放。 这里也许有人会问,如果我的项目很大,每个测试方法都需要连接数据库,在每个方法执行的时候进行连接再释放,这样是不是太耗资源太慢了,能不能在一个单元测试类实例化的时候就运行一个指定的方法呢? 
                         这是可以的。在NUnit中,我们使用[TestFixtureSetUp]和[TestFixtureTearDown]就可以实现这样的功能。[TestFixtureSetUp]是指在这个测试类的整个生命周期中,它在所有的测试方法之前运行一次,而[TestFixtureTearDown]是在所有的测试方法都结束时运行。 
                         这里要注意的,[TestFixtureSetUp]与构造函数是不一样的,它标识的方法迟于构造函数运行。我们再对这个测试类进行重构: 
                           
                            | [TestFixture] public class CalculatorTest
 ...{
 private Calculator cal;
 private int a, b, expected, actual;
     public CalculatorTest()...{
 Console.WriteLine("执行构造函数");
 }
     [TestFixtureSetUp]public void InitClass()
 ...{
 Console.WriteLine("执行TestFixtureSetUp");
 cal 
                                = new Calculator();
 a = 
                                10;
 b = 
                                2;
 }
 [TestFixtureTearDown]
 public void FinalizeClass()
 ...{
 Console.WriteLine("执行TestFixtureTearDown");
 }
     [SetUp]public void InitMethod()
 ...{
 Console.WriteLine("执行SetUp");
 }
     [TearDown]public void FinalizeMethod()
 ...{
 Console.WriteLine("执行TearDown");
 a = 
                                10;
 b = 
                                2;
 }
     [Test]public void TestAdd()
 ...{
 Console.WriteLine("TestAdd() 
                                Begin");
 expected 
                                = 12;
 actual 
                                = cal.Add(a, b);
 Assert.AreEqual(expected, 
                                actual);
 Console.WriteLine("TestAdd() 
                                End");
 }
 [Test]
 public void TestMinus()
 ...{
 Console.WriteLine("TestMinus() 
                                Begin");
 expected 
                                = 8;
 actual 
                                = cal.Minus(a, b);
 Assert.AreEqual(expected, 
                                actual);
 Console.WriteLine("TestMinus() 
                                End");
 }
 [Test]
 public void TestMultiply()
 ...{
 Console.WriteLine("TestMultiply() 
                                Begin");
 expected 
                                = 20;
 actual 
                                = cal.Multiply(a, b);
 Assert.AreEqual(expected, 
                                actual);
 Console.WriteLine("TestMultiply() 
                                End");
 }
 [Test]
 public void TestDivide()
 ...{
 Console.WriteLine("TestDivide() 
                                Begin");
 expected 
                                = 5;
 actual 
                                = cal.Divide(a, b);
 Assert.AreEqual(expected, 
                                actual);
 Console.WriteLine("TestDivide() 
                                End");
 }
 }
 |  在NUnit中,我们可以很清楚地看到这个类的执行顺序:    假如我们的测试项目中有使用到数据库,就可以把数据库连接写在[TestFixtureSetUp]中,把释放的代码写在[TestFixtureTearDown]中。 我相信现在大家对NUnit的这4个属性都应该有一个直观的认识了吧。都是4个很简单的属性,但是在使用中用处却是非常大的。 
                         接下来再为大家介绍几个常用的属性。  现在的测试中,我们有4个测试方法,但是如果我们想让其中的一个测试方法不在NUnit中显示,怎么办呢?不是注释,大家不要想歪了,注释大家都知道。要想让一个测试方法不在NUnit中显示,也不运行,我们应该使用[Ignore]属性。看看把TestAdd()添加[Ignore]属性后会是什么样子: 
                         
                           
                            | [Test] [Ignore]
 public void TestAdd()
 {
 Console.WriteLine("TestAdd() 
                              Begin");
 expected = 
                              12;
 actual = cal.Add(a, b);
 Assert.AreEqual(expected, actual);
 Console.WriteLine("TestAdd() 
                              End");
 }
 |    现在有了一个新的颜色了——黄色。它是指被忽略的方法。当然,你在项目中出现最多的肯定是绿色。在NUnit中我们可以用[Ignore]的重载方法[Ignore("忽略原因")]来定义忽略原因。 NUnit有一个与[Ignore]类似的属性[Explicit],它是指只有在NUnit中被明确的指定时才运行,否则不运行。有点拗口,我们来看例子。改写TestMinus方法: 
                           
                            | [Test,Explicit] public void TestMinus()
 {
 Console.WriteLine("TestMinus() 
                              Begin");
 expected = 
                              8;
 actual = cal.Minus(a, 
                              b);
 Assert.AreEqual(expected, 
                              actual);
 Console.WriteLine("TestMinus() 
                              End");
 }
 |  这里, 和 是完全一样的。 我们看它的截图:   "TestMinus"是灰色的,运行的Cases有2个,一个被忽略。而当我们选中TestMinus时:   这个测试运行了。 
                         再给大家介绍一个分类属性[Category(string name)],利用这个分类属性,我们可以为每个方法定义类别。 
                         
                           
                            | [Test, Ignore("Ignore"), Category("Category 
                              A")] public void TestAdd()
 ...{
 Console.WriteLine("TestAdd() 
                              Begin");
 expected = 
                              12;
 actual = cal.Add(a, b);
 Assert.AreEqual(expected, actual);
 Console.WriteLine("TestAdd() 
                              End");
 }
 [Test, Category("Category B")]
 [Explicit]
 public void TestMinus()
 ...{
 Console.WriteLine("TestMinus() 
                              Begin");
 expected = 
                              8;
 actual = cal.Minus(a, 
                              b);
 Assert.AreEqual(expected, 
                              actual);
 Console.WriteLine("TestMinus() 
                              End");
 }
 [Test, Category("Category A")]
 public void TestMultiply()
 ...{
 Console.WriteLine("TestMultiply() 
                              Begin");
 expected = 
                              20;
 actual = cal.Multiply(a, 
                              b);
 Assert.AreEqual(expected, 
                              actual);
 Console.WriteLine("TestMultiply() 
                              End");
 }
 [Test, Category("Category B")]
 public void TestDivide()
 ...{
 Console.WriteLine("TestDivide() 
                              Begin");
 expected = 
                              5;
 actual = cal.Divide(a, 
                              b);
 Assert.AreEqual(expected, 
                              actual);
 Console.WriteLine("TestDivide() 
                              End");
 }
 |  重新生成项目,在NUnit中,我们可以看到:    这里有我们定义的两个分类,我们选中"Category A",切换回"Tests"点"Run",我们看:   只测试了我们设置的"Category A"的一个方法,另一个方法是因为我们设置了[Ignore]所以没有执行测试。 好,到这里,我们已经把NUnit主要的属性学完了,接下来的章节我们将从实例出发学习NUnit。 本章示例代码下载>> 
                       |