NUnit单元测试笔记Ⅱ--基础篇
时间:2010-09-30 来源:⑩世特
使用NUnit编写测试
构建单元测试
如果你有一个名为CreateAccount 的被测试函数,俺么你的第一个测试函数的名称也许就是CreateSimpleAccount等等。它会以恰当的参数调用CreateAccount并验证CreateAccount的行为是否和它宣称的一样。当然你也可以有许多测试方法来执行CreateAccount(毕竟,不是所有account都是简单) 。
<截图来自<单元测试之道>>
测试代码仅限于我们内部使用。客户或者最终用户永远都不会看到,更不会使用这些代码。因此,你懂的。
测试代码必须要做一下这几件事情:
● 准备测试所需要的各种条件(创建所有必须的对象,分配必要资源等等)
● 调用要测试的方法
● 验证被测试方法的行为和期望是否一致。
● 完成后清理各种资源。
NUnit各种断言
断言是单元测试最基本的组成部分。因此,NUnit程序库以Assert类的静态方法的形式提供了不同形式的多种断言。
■ AreEqual
Assert.AreEqual(expexted, actual[,string message])
这是使用的最多的断言形式,在上面的参数中,expected是你的期望值(通常是硬编码的),actual是被测试代码实际产生的值,message是一个可选的消息,如果提供的话,就爱你工会在发生错误的时候报告这个消息。
计算机并不能精确的表示所有的浮点数,通常会有一下偏差。因此,如果你想用断言来比较浮点数(float或者是double)则需要指定一个额外的误差参数。它表明你需要多接近才能认为两数“相等”。
■ IsNull
Assert.IsNull(object [ , string message])
Assert.IsNotNull(object [ , string message])
验证一个给定的对象是否为null,如果答案为否,则将会失败。Message参数是可选的。
■ AreSame
Assert.AreSame(expected, actual [ , string message])
验证expected参数和actual参数所引用的是否为同一个对象,如果不是的话,将会失败。Message参数是可选的。
■ IsTrue
Assert.IsTrue(bool condition , actual [ , string message])
验证给定的二元条件是否为真,如果假的话,将会失败。Message参数是可选的
■ Fail
Assert.Fail([string messge])
上面的断言将会使测试立即失败,其中message参数是可选的。这种断言通常被用于标记某个不应该被到达的分支,但它在实际中并不常用。
NUnit框架
下面是一段简单的测试代码,它展示了开始使用该框架的最小要求:
using NUnit.Framework; [TestFixture] public class TestLargest { [Test] public void LargestOf3() { Assert.AreEqual(-7, Cmp.Largest(new int[] { -9, -8, -7 })); } }
首先,第一行的using声明引入了必须的NUnit类。NUnit框架提供了我们需要的单元测试的功能,包括所有我们在前面描述的断言方法。
接下来,我们看到【TestFixture】特性标记。而且这个类必须声明为public,而且它必须有一个public的、没有参数的构造函数(默认构造函数就行了)。
最后,测试类包含了用【Test】特性标记的方法。所有用【Test】标记的public方法都会被NUnit自动运行。
NUnit测试的组成
如我们刚才看到的一个【TestFixture】标记的类包含一个或多个测试方法:每个方法包含一个或多个断言。一个程序集可以包含多个testFixture。
我们可以把现存的多个testFixture 组合进一个testSuite中,一个testSuite是一些testFixture类的集合。用【Suite】特性标记。
Suite是层次化组织测试的一个很有用的机制。特别是对于无人照料的构建,用它来调整测试集合是非常方便的。
■ Categories
NUnit提供了另一种机制Categories,你可以用它来对每个单独的测试方法和testFixtur分门别类。一个category是指你定义的一个名字。你可以把不同的测试方法关联到一个或多个category,然后在运行测试的时候,选择你想要运行的category。
比如,你可以使用category来把运行时间较短的测试和运行时间较长的测试分开。显然,前一种测试将经常被运行,后一种则可能只是在夜晚构建的时候才运行一次。
Category是以attribute特性的形式来指定的。当声明方法的时候,你可以提供一个祖父来指定它的category。然后,在你运行测试的时候,可以指定你要运行那些category(你可以一次执行运行多个category)。
namespace UnitTest { [TestFixture] public class TestLargest { [Test] [Category("Short")] public void LargestOf3() { Assert.AreEqual(-7, Cmp.Largest(new int[] { /*......*/})); } [Test,Category("Short")] public void LargestOf30() { Assert.AreEqual(77,Cmp.Largest(new int[]{ /*......*/ })); } [Test] [Category("Long")] public void LargestOf3000() { Assert.AreEqual(77, Cmp.Largest(new int[] { /*......*/ })); } } }
你可以分开2行指定多个特性(test和category),也可以写在1行。
打开NUnit,我们可以看到category选项卡中有short和long,你只需选中要执行的,添加到下面的list里,再excute一下就ok了。
■ Per-method的Setup和Teardown
每个测试的运行都应该是相互独立的;从而你就可以在任何时候,以任意的顺序运行每个单独的测试。
为了获得这样的好处,在每个测试开始之前,你都需要重新设置某些测试环境,或者在测试完成之后,你需要释放一些资源。借助于一些Attribute,NUnit让你指定2种方法,分别用于环境的建立和清理:
[SetUp] public void MySetup() {/* ..... */ } [TearDown] public void MyTeardown() {/* ..... */ }
在这个例子中,在调用每个【Test】方法之前,调用方法MySetup;并且在每个测试方法完成之后,调用方法MyTeardown()。
比如,如果对于每个测试你都需要某种数据库连接。这时,你就不需要在每个测试方法中重复建立连接和释放连接了,而只须在SetUp和TearDown方法中分别建立和释放连接:
[TestFixture] public class TestDB { private Connection dbConn; [SetUp] public void MySetup() { dbConn = new Connection("oracle", 1521, "user", "psw"); dbConn.Connect(); } [TearDown] public void MyTeardown() { dbConn.Disconnect(); dbConn = null; } [Test] public void TestAccountAccess() { } [Test] public void TestEmployeeAccess() { } }
■ Per-class的Setup和Per-class的Teardown
知道了Per-method,那就容易理解Per-class了。
[TestFixtureSetUp] public class OneTimeSutup {} [TestFixtureTearDown] public class OneTimeTeardown { }
结合刚才的两个就是