单元测试框架NUnit 之 Attributes特性(一)
最早的时候,Nunit使用继承和命名约定来确认方法或类是用来测试的。但从2.0开始,开始使用了自定义特性custom attributes:这样你不必继承一个特定的类,可以自由的规定类的层次;没有了命名约定,你可以按方法的意图来命名。
所有的NUnit特性attributes都包含在NUnit.Framework的命名空间中,因此每个文件都应该引用这个命名空间,你的测试项目也应该引用nunit.framework.dll的程序集。
从2.4.6开始,NUnit特性attributes不再是封闭的sealed,任何继承自它们的特性attributes都会被识别。
Nunit提供了36种可供使用的特性attributes:
1,CategoryAttribute (NUnit 2.2)
分类特性Category attribute提供了一种方法让测试按分组的方式运行。无论是测试类或方法都可以指定为一个特定的分类。gui和console的运行器都可以指定在运行中包含或排除的分类:这样只有指定分类的测试类或方法运行,其它的也不会被报告。Console运行器通过/include 或者 /exclude的参数来指定分类,gui运行器有一个Categories的标签:它会列出所有的分类,让你选择要运行的分类。
namespace NUnit.Tests { using System; using NUnit.Framework; [TestFixture] [Category("LongRunning")] public class LongRunningTests { [Test] [Category("Long")] public void VeryLongTest() { /* ... */ } } }
2,CombinatorialAttribute (NUnit 2.5)
CombinatorialAttribute 用在测试方法上,Nunit会用方法和参数的各个项的组合生成测试用例。此属性是默认调用的。
如
[Test, Combinatorial] public void MyTest( [Values(1,2,3)] int x, [Values("A","B")] string s) { ... }
将会按以下的方式运行6次:
MyTest(1, "A") MyTest(1, "B") MyTest(2, "A") MyTest(2, "B") MyTest(3, "A") MyTest(3, "B")
3,CultureAttribute (NUnit 2.4.2)
Culture attribute用来指定测试类或方法运行时的区域信息.。它不会对配置产生影响,主要是用来确认是否要执行这个测试。你如果想在运行时对改变配置,可以用SetCulture attribute。
如果指定的区域信息不被支持,它会被跳过。在gui运行器中,这个测试结点依然是灰色,不会对状态条产生影响。
你可这样调用,来加到相应的测试类或方法上:
[Culture("fr-FR")] [Culture(Exclude="en,de")]
4,Description (NUnit 2.4)
Description attribute 是给测试程序集、类或方法添加一个描述。这个描述会出现在输出的XML文件,显示在测试的属性框中。
[assembly: Description("Assembly description here")] namespace NUnit.Tests { using System; using NUnit.Framework; [TestFixture, Description("Fixture description here")] public class SomeTests { [Test, Description("Test description here")] public void OneTest() { /* ... */ } } }
5,ExpectedExceptionAttribute (NUnit 2.0 plus Updates)
使用它,你可以指定测试运行中将会抛出的异常。它有几个参数和命名参数,你可以以下几种方式调用它。
指定期望异常的类型:当指定的异常抛出时,测试成功。
// 2.0时引入的,必须是一个确定的类型 [ExpectedException( typeof( ArgumentException ) )] public void TestMethod() { ... } //从2.2.4时,以字符串的方式指定类型而不用引用异常的程序集 [ExpectedException( "System.ArgumentException" ) )] public void TestMethod() { ... }
确定异常的错误信息:从2.1开始引入了一个传入第二个参数的构造指定异常的message属性的值;从2.2.4,同样作用的用一个字符串参数来表示异常类型的构造函数可用;从2.4开始以下的方式全部被标记为不推荐使用,而一个新的以命名参数的方式用来替代。
// Obsolete form: [ExpectedException( typeof( ArgumentException ), "expected message" )] [ExpectedException( "System.ArgumentException", "expected message" )] // Prefered form: [ExpectedException( typeof( ArgumentException ), ExpectedMessage="expected message" )] [ExpectedException( "System.ArgumentException", ExpectedMessage="expected message" )]
从2.4开始,我们可以对message进行更多的测试,而不仅仅是相等。这需要名字为MatchType的命名参数:它是一个枚举类型。
public enum MessageMatch { /// 期望完全相等 Exact, /// 期望message包含给定字符串 Contains, /// 期望message符合给定的正则 Regex, /// 期望message以给定的字符串开始 StartsWith }
例如你可以这样调用,达到期望抛出异常的message信息包含”unspecified”:
[ExpectedException( typeof( ArgumentException), ExpectedMessage="unspecified", MatchType=MessageMatch.Contains )] public void TestMethod() { ... }
通过UserMessage的命名参数,你还可以自定义当ExpectedException attribute的条件没被满足时的提示信息。
[ExpectedException( typeof( ArgumentException ), UserMessage="Custom message" )] public void TestMethod() { ... }
相等对实际中try/catch来处理异常,这样用特性的声明显然是复杂了。因此,从2.4开始,你可以指定一个方法来处理异常。
通用异常处理器应该实现IExpectExceptionInterface:
public interface IExpectException { void HandleException( System.Exception ex ); }
这个处理器只有在被标记了ExpectedException特性的时才会被调用。你如果想要所有的异常都用这样的处理器处理,只需要指定一个不带参数的ExpectedException标记即可。
当然,你可能要对只对某个方法应用处理器,你可以通过Handler命名参数:
[ExpectedException( Handler="HandlerMethod" )] public void TestMethod() { ... } public void HandlerMethod( System.Exception ex ) { ... }
我们可以看到它并没有实现IExpectException。并且在以后,我们可以把这个处理器指定给任何需要用的方法,而其它的未指定而标记ExpectedException会调用通用处理器。
但是需要指出的是,这些处理方法可能检查异常并且对关联的属性做断言:任何失败的信息会和其它断言一样在测试结果中反馈出来。
6,ExplicitAttribute (NUnit 2.2)
除非被显示选择要被运行,应用Explicit attribute的类或方法,在运行时会被忽略。你可以指定可选参数来给出应用此特性的原因。
namespace NUnit.Tests { using System; using NUnit.Framework; [TestFixture, Explicit] public class ExplicitTests { [Test, Explicit] public void ExplicitTest() { /* ... */ } } }
上面的示例只是指出怎样调用,一般情况下不会即在类和其下的方法中同时应用,尽管它不会报错。
7,IgnoreAttribute (NUnit 2.0)
ignore attribute 的作用是让类或方法在测试时暂时不运行。gui中的状态栏会变为黄色并且给出这个测试未运行的报告。这可以用于测试临时不运行的情况,这比注释掉要好的多,因为这些代码仍然会被编译并且会有在运行时会有提示这些测试未被运行。
8,MaxtimeAttribute (NUnit 2.5)
MaxtimeAttribute 以毫秒为单位来指定测试方法运行的最大时间。如果运行时间大于指定的时间,将会报告失败。要注意的是,它不会阻止测试的运行,而是在运行完毕时,通过比较运行时间和指定的时间,因此代码中的其它断言的优先级高于时间的比较。如果要中止运行时间过长的测试,可以用TimeoutAttribute。
9,PairwiseAttribute (NUnit 2.5)
这个特性指定Nunit应该用参数的项中任意可能成对的值来生成测试用例,这是解决当参数多于三个时组合产生大量测试的好方法。但是在现在发布版本中,它可以用但是采用的是之前combinatorial attribute的工作方式。
10,PlatformAttribute (NUnit 2.2.2)
Platform attribute用来指定测试类或方法运行的平台,以不区分大小字符串的平台名字来指定要包含或排除的平台。如果运行的平台不符合属性指定的条件,这个测试将会被忽略且不会其它测试和状态产生任何影响。
[Platform("NET-2.0")] [Platform(Exclude="Win98,WinME")]
以下是支持的平台的名字:
- Win
- Win32
- Win32S
- Win32Windows
- Win32NT
- WinCE
- Win95
- Win98
- WinMe
- NT3
- NT4
- NT5
- NT6
- Win2K
- WinXP
- Win2003Server
- Vista
- Win2008Server
- Win2008ServerR2
- Windows7
- Unix
- Linux
- Net
- Net-1.0
- Net-1.1
- Net-2.0
- Net-4.0
- NetCF
- SSCLI
- Rotor
- Mono
- Mono-1.0
- Mono-2.0
11,PropertyAttribute (NUnit 2.4)
Property attribute 以键值对的形式为类或方法设置一个属性。
namespace NUnit.Tests { using System; using NUnit.Framework; [TestFixture, Property("Location",723)] public class MathTests { [Test, Property("Severity", "Critical")] public void AdditionTest() { /* ... */ } } }
它对测试的运行没有任何影响,只是会出现在输出的XML文件和gui中的测试属性框中。如果可能的话,你可以写一个扩展来处理这个指定的属性或者在一个测试中通过反射来获取这个值。
另外,你还可以继承PropertyAttribute创建自定义属性,子类中可以使用它提供了一个protected的构造函数用来创建一个属性并设定值。Nunit本身也采用了这种方法来创建一些属性。
12,RandomAttribute (NUnit 2.5)
RandomAttribute 根据测试方法的一个参数来生成一系列的随机数。有如下几种构造:
public Random( int count ); public Random( double min, double max, int count ); public Random( int min, int max, int count );
默认情况下,Nunit会参数中提供的所有数据生成一个测试用例,如下面的例子,将会被运行15次:每个X值都会有5个介于-1.0和1.0之前的随机数与之组合。
[Test] public void MyTest( [Values(1,2,3)] int x, [Random(-1.0, 1.0, 5)] double d) { ... }
13,RangeAttribute (NUnit 2.5)
RangeAttribute 用于测试方法的参数中,用来指定一系列数。有如下构造:
public RangeAttribute( int from, int to ); public RangeAttribute( int from, int to, int step ); public RangeAttribute( long from, long to, long step ); public RangeAttribute( float from, float to, float step ); public RangeAttribute( double from, double to, double step );
示例:
[Test] public void MyTest( [Values(1,2,3) int x, [Range(0.2,0.6,0.2] double d) { ... }
这个测试会被运行9次:
MyTest(1, 0.2) MyTest(1, 0.4) MyTest(1, 0.6) MyTest(2, 0.2) MyTest(2, 0.4) MyTest(2, 0.6) MyTest(3, 0.2) MyTest(3, 0.4) MyTest(3, 0.6)
下文介绍其它特性。