单元测试:Nunit(二)
作为单元测试的第二部分,和第一部分的间隔时间差的的确有点长了(嘿嘿,自己一直在偷懒)。在第一讲中主要讲的是如何在VS2005中进行单元测试,以及当时选NUnit2.48(.net 2.0)作为学习的版本,和进行了一个小个程序的测试,大致上都知道如何使用NUnit对C#编写的代码如何进行单元测试,而在这一讲中则是主要讲讲几个常用的属性(Attribute)。
先写一个小小的程序,功能只是在一个数组中找出一个最大的数字。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
if (null == array || 0 == array.Length)
{
throw new Exception("数组为空!");
}
int num = Int32.MinValue;
for (int i = 0; i < array.Length; i++)
{
if (num < array[i])
{
num = array[i];
}
}
return num;
}
同样要建立一个测试类,现在开始逐个介绍一些比较常用的属性。
一、[TestFixtureSetUp]这个属性标识的函数表示是这个类的初始化函数,测试类一旦执行,首先执行的就是这个属性标识的函数,既然是初始化,那当然一般是初始化一些对象和字段之类的,功能有点类似于构造函数。
public void Init()
{
largest = new Largest();
Console.WriteLine("我只会在类的最初调用时初始化对象!");
}
二、上面说了初始化,自然的我们要说到销毁了,[TestFixtureTearDown]与初始化对应,此属性标识的函数在类的最后用来销毁对象,根据功能很自然的就想到析构函数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public void Terminate()
{
if (null != largest)
{
largest = null;
}
Console.WriteLine("我只会在类的最后销毁对象!");
}
三、上面说的初始化和销毁都是针对整个测试类来说的,有没有一个可以针对没个测试的case的呢?可以在执行每个case之前进行初始化,在每个case执行之后又可以销毁,这样不就可以让一些公用的对象可以在执行每个case的时候都重新被赋值,不会相互影响。既然都这样说了,当然有了。[SetUp]属性标识的函数可以在每个Test Case的执行之前初始化一次。
public void InitCase()
{
largest = new Largest();
Console.WriteLine("我会在执行每个Test Case之前初始化对象!");
}
四、直接来看销毁[TearDown].
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public void TerminateCase()
{
if(null != largest)
{
largest = null;
}
Console.WriteLine("我会在执行完每个Test Case之后销毁对象!");
}
五、在程序中我们对应空数组抛出了一个异常,那么我们在测试的时候,如果输入的数组是空的会怎么样?看下面一段程序:
public void TestGetLargest1()
{
int[] numArr = new int[] { };
largest.GetLargest(numArr);
}
很明显,测试肯定会抛出一个异常,不能通过。
那现在我们知道这样输入是不对,那怎么可以屏蔽掉这个异常呢。[ExpectedException],这个实行有几个重载的类型,根据情况自己选择。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[ExpectedException(typeof(Exception))]
public void TestGetLargest2()
{
int[] numArr = new int[] { };
largest.GetLargest(numArr);
}
这个时候我们看到的结果不再是提示有异常抛出,而是通过了测试(你都已经预料到有这种异常了,当然不会再提示你程序会抛出这个异常!)。
六、最后简单的提一下其他几个属性:[Ignore]标识的Test Case不会再NUnit的IDE中执行,因为被你忽略掉了。[Explicit]必须要显示的选中这个Case才会被执行,否则是不会执行的。[Category]分组属性,可以将几个Case分到同一个组中,这样就会以组为单位去执行,或不执行。
测试类完整代码贴上:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class LargestTest
{
private Largest largest;
[SetUp]
public void InitCase()
{
largest = new Largest();
Console.WriteLine("我会在执行每个Test Case之前初始化对象!");
}
[TearDown]
public void TerminateCase()
{
largest = null;
Console.WriteLine("我会在执行完每个Test Case之后销毁对象!");
}
[TestFixtureSetUp]
public void Init()
{
largest = new Largest();
Console.WriteLine("我只会在类的最初调用时初始化对象!");
}
[TestFixtureTearDown]
public void Terminate()
{
if (null != largest)
{
largest = null;
}
Console.WriteLine("我只会在类的最后销毁对象!");
}
[Test]
public void TestGetLargest()
{
int[] numArr = new int[] { -1, 0, 2, 4, -10 };
int result = largest.GetLargest(numArr);
Assert.AreEqual(4, result);
}
[Test]
public void TestGetLargest1()
{
int[] numArr = new int[] { };
largest.GetLargest(numArr);
}
[Test]
[ExpectedException(typeof(Exception))]
public void TestGetLargest2()
{
int[] numArr = new int[] { };
largest.GetLargest(numArr);
}
[Test]
public void TestGetLargest3()
{
int[] numArr = new int[] { 10, 10 };
int result = largest.GetLargest(numArr);
Assert.AreEqual(10, result);
}
[Test]
[Ignore]
public void TestGetLargest4()
{
int[] numArr = null;
int result = largest.GetLargest(numArr);
}
}
执行结果:
不知道有没有发现个问题,对于这个程序中,测试案例会很多,比如说数组的数全是正数,负数,有正有负,全部数相同时等等,这样不就要写很多重复的测试case,应该有其他的方法吧。呵呵,不妨试试利用XML、文本存储数据来实现。下一部分我准备用XML读取数据来实现。