单元测试框架NUnit 之 Attributes特性(二)

 

接上篇

 

14,RepeatAttribute (NUnit 2.5)

RepeatAttribute 是用来指定一个测试用例应该运行几次。如果任何一次失败,以下的次数将不会运行并只报告一次错误。

15,RequiredAddinAttribute (NUnit 2.5)

RequiredAddin attribute 用来指定一个程序集如果要正确运行所需要的程序集。如果指定的程序集没有被引用,整个程序将会标记为不可执行的。

在3个试用版本中,这个特性可以被应用到类或方法上,但是从下个版将会限定只用到程序集。

示例:

[assembly: RequiredAddin("MyTestFixtureAddin")]

[assembly: RequiredAddin("MyTestAddin")]

[assembly: RequiredAddin("MyDecoratorAddin")]



...



namespace NUnit.Tests

{

  using System;

  using NUnit.Framework;



  [MyTestFixture]

  public class MyTests

  {

    [MyTest]

	public void SomeTest()

	{

	  ...

	}

  }

  

  [TestFixture, MyDecorator]

  public class MoreTests

  {

    [Test, MyDecorator]

	public void AnotherTest()

	{

	  ...

	}

  }

}

16,RequiresMTAAttribute (NUnit 2.5)

RequiresMTAAttribute 应用在方法、类或程序集上,指明这段代码要运行在多线程套间(multi-threaded apartment):如果父测试不是MTA线程中运行,会创建一个新的MTA线程。

17,RequiresSTAAttribute (NUnit 2.5)

它要求测试运行在单线程套间(Single-threaded apartment)。它也会创建一个新的STA线程如果父测试不是在STA线程中运行。

18,RequiresThreadAttribute (NUnit 2.5)

它要求应用此特性方法、类或程序集应该运行在独立的线程中。你也可以在构造中加入一个可选的参数用来指明线程的ApartmentState。用此方法,不管你带参数或不带,都会创建一个新的线程,而RequiresSTAAttribute or RequiresMTAAttribute只会在当前线程的ApartmentState不适合时才会创建。

// 运行这个程序集的测试都会被创建新的线程

[assembly:RequiresThread]



...



// 测试类

[TestFixture, RequiresThread]

public class FixtureOnThread

{

  // 该类中的所有方法都会创建一个独立的线程

}



[TestFixture]

public class AnotherFixture

{

  [Test, RequiresThread]

  public void TestRequiringThread()

  {

    // 这个测试运行要求一个独立的线程

  }

  

  [Test, RequiresThread(ApartmentState.STA)]

  public void TestRequiringSTAThread()

  {

    // 一个独立的STA线程将会创建用来运行这个测试

  }

}

19,SequentialAttribute (NUnit 2.5)

SequentialAttribute应用一个测试方法,会用参数提供的数据项生成测试用例,每个数据只用一次。如果一个参数是带多重属性的,数据项的顺序不确定的。但是,对于一个给定的系统和运行时,这个顺序是不变的。

如:

[Test, Sequential]

public void MyTest(

    [Values(1,2,3)] int x,

    [Values("A","B")] string s)

{

    ...

}

将会按以下方试运行三次:

MyTest(1, "A")

MyTest(2, "B")

MyTest(3, null)

20,SetCultureAttribute (NUnit 2.4.2)

这个属性会改变当前测试的区域信息,可被应用到方法和类级别。要注意的是,这个改变的影响直到整个类或方法执行完毕然后再设定为初始值。如果只想确定在当前区域信息下是不是执行这个测试,请用Culture attribute。

只能确定一个区域信息,在多个区域信息中运行测试将在未来的版本中增加。不过,通过构建你的测试代码到一个私有方法,然后在各个不同区域的测试方法中调用,仍然可以达到相同的目的。

21,SetUICultureAttribute (NUnit 2.5.2)

SetUICulture attribute是用来设定UI的区域信息的,用法参照上面的SetCultureAttribute。

22,SetUpAttribute (NUnit 2.0 / 2.5)

这个特性应用在测试类内部提供一些在每一个测试方法调用之前都会执行的功能。在Nunit2.5之前,要求只能有一个SetUp标记的方法并且要求是一个实例方法。从2.5开始,可以是实例或静态的,并且可以有多个:多个SetUp方法尽量定义在继承的不同层次上。也就是说,SetUp方法是可以被继承的,父类定义的SetUp方法,在子类测试方法调用之前也会被调用。在2.5之前,限定只能有一个SetUp方法,你如果想使用基类的SetUp功能并添加自己的功能,你应该在子类中自行调用基类的SetUp方法。从2.5之后,你只需要在子类中添加你想要附加的功能,NUnit会在子类方法调用之前调用基类的SetUp方法。尽量避免在一个类中定义多个SetUp方法,因为它们的执行顺序是无法预期的。

如果SetUp方法失败或抛出异常,这个测试就会被停止执行并且报告失败或抛出异常。

23,SetUpFixtureAttribute (NUnit 2.4)

此特性标记当前命名空间的这个类包含只执行一次one-time的setup和teardown方法。被标记的类最多只能有一个带有SetUpAttribute的方法和一个带有TearDownAttribute的方法。这个类的SetUp方法会在这个命名空间的任何测试类执行之前执行一次,TearDown方法会在这个命名空间下所有测试都执行完后执行一次。

一个命名空间下只能有一个SetUpFixture,任何命名空间之外的SetUpFixture会把这些功能应用到整个程序集。

这个类应该是public的并且应该有一个默认的构造函数以确保Nunit能构造这个类的实例。

24,SuiteAttribute (NUnit 2.0/2.4.4)

Suite Attribute是用来定义包含一系列测试的集合,它只有在命令行应用/fixture 选项时运行。从2.0以后引入用来替代以前的从TestSuite类继承的方法。

Suite机制依赖于一个标记了SuiteAttribute的静态属性。2.0之后,它会返回一个TestSuite的值。这个方式有一些问题:它需要引用一个用户平常不需要用到的nunit.core程序集:这样需要重新编译才能把测试移植到其它版本,并且在不同的core程序集版本可能出现运行不正常的现象。

从2.4.4之后,标记SuiteAttribute的属性将会返回一个包含测试类对象或类型的集合,另外可以包含TestFixtureSetUpTestFixtureTearDown 的方法,来为suite的测试类提供一次性(one-time) setup 和 teardown机制。

//  2.0的使用方式

namespace NUnit.Tests

{

  using System;

  using NUnit.Framework;

  using NUnit.Core;



  public class AllTests

  {

    [Suite]

    public static TestSuite Suite

    {

      get

      {

        TestSuite suite = new TestSuite("All Tests");

        suite.Add(new OneTestCase());

        suite.Add(new Assemblies.AssemblyTests());

        suite.Add(new AssertionTest());

        return suite;

      }

    }

  }

}

//新的使用方式

namespace NUnit.Tests

{

  using System;

  using NUnit.Framework;



  private class AllTests

  {

    [Suite]

    public static IEnumerable Suite

    {

      get

      {

        ArrayList suite = new ArrayList();

        suite.Add(new OneTestCase());

        suite.Add(new AssemblyTests());

        suite.Add(new NoNamespaceTestFixture());



         // 也可采用这样的方式

        //suite.Add(typeof(OneTestCase));

        //suite.Add(typeof(AssemblyTests));

        //suite.Add(typeof(NoNamespaceTestFixture));

        return suite;

      }

    }

  }

}

注意:

1,不能直接包含单独的测试方法在suite中,如果想这样做,请使用较早的方式并创建一个继承自NUnit.Core.TestCase的对象,但这是不推荐的。

2,默认情况下,suites是不会被执行的,它提供的目的就是在所有运行级别高于所有测试的测试集合,这些只会在在命令行中应用/fixture选项时才执行。

以后的版本可能会移除这两条限制。

25,TearDownAttribute (NUnit 2.0 / 2.5)

同SetUp用法一致,只不过它是在每一个测试方法运行之后运行的。

26,TestAttribute (NUnit 2.0 / 2.5)

这个属性标记测试类中的一个方法是测试方法。为了向后兼容,如果一个方法以“test”开始,也会被认为是一个测试方法,不区分大小写,但这可以在测试的配置文件中设置不支持这种方式。

从2.5开始,测试方法可以是静态的,并且可以带有参数和返回值了,因为Nunit已经知道怎么去处理参数和返回值。

关于测试方法的参数,像我们之前提到的多数据项的参数会创建多个测试用例,有一些特性允许以内联的方式确定参数,而一些可以提供一个独立的方法、属性或字段。此外,还有一些属性会提供方法的所有参数,而有些只为一个参数提供数据。如下表:

  提供方法的所有参数 为一个参数提供数据

内联方式
TestCaseAttribute RandomAttribute
RangeAttribute
ValuesAttribute
独立方式 TestCaseSourceAttribute ValueSourceAttribute

还有一些特性如CombinatorialAttribute(default) ,PairwiseAttribute,SequentialAttribute加到方法上去指定如何处理这些参数的。

如果测试方法签名不正确,会被认为是不可运行的,会被运行器指出。

27,TestCaseAttribute (NUnit 2.5)

TestCaseAttribute 会为一个测试方法提供所有参数,让方法用这些参数完成一次测试。

如下面例子:DevideTest会按每个TestCase指定的参数分配给相应的参数,来执行一次测试。

[TestCase(12,3,4)]

[TestCase(12,2,6)]

[TestCase(12,4,3)]

public void DivideTest(int n, int d, int q)

{

  Assert.AreEqual( q, n / d );

}
执行的结果就是:
DivideTest(12,3,4);

DivideTest(12,2,6);

DivideTest(12,4,3);

如果提供的数据格式和方法运行所需要的参数不一致时,参数提供给方法运行时要先用Convert.ChangeType()做转型。特性可能出现多次,并可以和其它特性同时使用。

除此之外,可以用一个Result的命名参数来指定返回值。

[TestCase(12,3, Result=4)]

[TestCase(12,2, Result=6)]

[TestCase(12,4, Result=3)]

public int DivideTest(int n, int d)

{

  return( n / d );

}

Nunit会检测返回值与给定的值是不是相等。

还有其它一些命名参数可供使用:

Description

设定这次测试的描述。

ExpectedException

此次运行应该抛出的异常的类型。

ExpectedExceptionName

此次运行应抛出的异常的全名。

ExpectedMessage

抛出异常的message信息。

MatchType

MessageMatch 枚举值指出message值的匹配方式。 (参考 ExpectedExceptionAttribute)

TestName

这次测试的名字,如果未指定方法名和本次运行指定的参数会被组合起来做为名字。

Ignore

设为true时,将忽略本次测试运行。

IgnoreReason

指定忽略的原因。如果不是一个空值,默认就会忽略本次运行。

另外给定多个TestCaseAttribute时,对于不同的系统或平台执行的顺序可能会不一样。

28,TestCaseSourceAttribute (NUnit 2.5)

TestCaseAttribute 特性一样都是为测试提供数据,但TestCaseSourceAttribute以独立的方式提供数据。它有两个构造函数:

TestCaseSourceAttribute(Type sourceType, string sourceName);

TestCaseSourceAttribute(string sourceName);

如果sourcetype参数指定了,这表示的是提供数据的类。它应该有个默认的构造函数。没指定时,包含的这个测试的类将会被构建。

sourcename参数指定数据源的名字:这可以是字段、属性或方法,可以是实例也可以是静态的,但是他必须返回一个IEnumerable或者实现了IEnumerable的对象,也必须符合测试方法签名参数的要求。

对于如何构建测试用例,Nunit用以下的枚举器返回的每项数据:

1.如果对象实现了NUnit.Framework.ITestCaseData,它的属性可被用于每个用例。以下是它公开的字段或属性:

Arguments

一个object[] 类型的对象,它会传给方法用做参数。

Categories

一个IList类型被应用到各个用例的分类。

Description

设定测试的描述属性。

ExpectedException

此次调用应该抛出的异常。

ExpectedExceptionName

抛出异常的命名。

Properties

一个 IDictionary 类型也会应用给各个测试用例。这些值使用PropertiesAttribute使用是一致的。

Result

期望的测试方法的返回值,返回值类型要兼容。

TestName

测试的名字,如果没有提供时,方法名字和传入的参数值组成这个名字。

Ignored

设为true时,这个测试用例会被忽略。

IgnoreReason

忽略的原因,如果不是空值,这个测试用例会被忽略。

2.如果是一个object[]对象,它的成员的将做为方法的参数。

[Test, TestCaseSource("DivideCases")]

public void DivideTest(int n, int d, int q)

{

    Assert.AreEqual( q, n / d );

}



static object[] DivideCases =

{

    new object[] { 12, 3, 4 },

    new object[] { 12, 2, 6 },

    new object[] { 12, 4, 3 } 

};

3.如果其它类型的数组,Nunit也可以使用只要这个类型与方法参数的类型匹配,如上个例子,如果是int[]的交错数组也行。

尽管你可以用实现ITestCaseData的对象来实现为测试提供更多额外的信息,但是Nunit仍然提供了TestCaseDate类来达到同样的目的。

[TestFixture]

public class MyTests

{

  [Test,TestCaseSource(typeof(MyFactoryClass),"TestCases")]

  public int DivideTest(int n, int d)

  {

    return n/d;

  }

	

  ...

}



public class MyFactoryClass

{

  public static IEnumerable TestCases

  {

    get

    {

      yield return new TestCaseData( 12, 3 ).Returns( 4 );

      yield return new TestCaseData( 12, 2 ).Returns( 6 );

      yield return new TestCaseData( 12, 4 ).Returns( 3 );

      yield return new TestCaseData( 0, 0 )

        .Throws(typeof(DivideByZeroException))

        .SetName("DivideByZero")

        .SetDescription("An exception is expected");

    }

  }  

}

当然对于最后一个yield代码块你可以:

 TestCaseData data = new TestCaseData(0,0);

      data.ExpectedException = typeof(DivideByZeroException;

      data.TestName = "DivideByZero";

      data.Description = "An exception is expected";

      yield return data;

TestCaseData提供以下方法和属性,你可以按任意顺序调用:

.Returns.SetCategory(string).SetProperty(string, string).SetProperty(string, int).SetProperty(string, double).SetDescription(string).SetName(string).Throws(Type).Throws(string).Ignore().Ignore(string)

另外,TestCaseSourceAttribute提供的数据源被调用的顺序也不是可预测的。

你可能感兴趣的:(attribute)