NUnit扩展ICommandWrapper如何包装TestCase?

时间:2019-05-23 14:26:44

标签: c# nunit nunit-3.0

我尝试根据本文https://www.skyrise.tech/blog/tech/extending-nunit-3-with-command-wrappers/扩展扩展ICommandWrapper。我发现我也可以扩展TestAttribute,并且可以正常工作,然后我尝试扩展TestCaseAttribute

[AttributeUsage(AttributeTargets.Method), AllowMultiple = true]
public class MyTestCaseAttribute : TestCaseAttribute, IWrapSetUpTearDown
{
    private object[] _args;

    public MyTestCaseAttribute(params object[] args) : base(args)
    {
        _args = args;
    }

    public TestCommand Wrap(TestCommand command)
    {
        return new MyTestCommand(command, _args);
    }
}

MyTestCommand扩展了DelegatingTestCommand,就像在文章中一样。 问题是,如果我将多个MyTestCaseAttribute添加到一个测试方法中,则该测试方法会被MyTestCommand.Execute的代码多次包装。

[EDIT] 示例:
假设MyTestCommand看起来像这样:

public abstract class MyCommandDecorator : DelegatingTestCommand
{
    public override TestResult Execute(TestExecutionContext context)

    private object[] _testCaseArgs;

    protected TestCommandDecorator(TestCommand innerCommand, params object[] args) : base(innerCommand)
    {
        _testCaseArgs = args;
    }

    public override TestResult Execute(TestExecutionContext context)
    {
        DoSomething(_testCaseArgs);
        return context.CurrentResult = innerCommand.Execute(context);
    }
}

假设我用两个[MyTestCase]属性装饰测试方法:

[MyTestCase(1)]
[MyTestCase(2)]
public void MyTest(int foo)
{
//...
}

所需的行为类似于:

DoSomething(1);
MyTest(1);
DoSomething(2);
MyTest(2);

但是实际行为是:

DoSomething(2)
DoSomething(1)
MyTest(1)
DoSomething(2)
DoSomething(1)
MyTest(1)

2 个答案:

答案 0 :(得分:1)

问题的关键是... C#允许您用属性装饰方法或类。但是在NUnit之外不存在单独的测试用例-没有C#等效项-因此您无法装饰它。

IOW,您的两个属性将应用于该方法,并使NUnit使用该方法生成两个测试用例。但是,您的属性还实现了ICommandWrapper,这会导致NUnit包装它生成的任何测试用例。 NUnit的一部分正在寻找创建测试用例,另一部分正在寻找用于包装测试用例的属性。这两个部分完全分开。

这就是NUnit在测试用例方法上使用属性来指示诸如忽略用例之类的原因的原因。它不能使用属性,因为属性将应用于该方法生成的每个测试用例

希望能解释发生了什么事。

要解决该问题,您的命令包装器应仅将自身应用于由该特定属性实例生成的测试。这意味着您必须参与测试的创建,至少要在您的属性记住对它创建的测试的引用的范围内。这有点复杂,但是您应该查看TestCaseAttribute的代码以了解如何创建测试用例。

答案 1 :(得分:0)

想通了。

除了扩展TestCaseAttribute之外,我还可以扩展TestAttribute并获取使用TestCaseAttribute从标准command.Test.Arguments传递给包装类的参数。

[AttributeUsage(AttributeTargets.Method), AllowMultiple = true]
public class MyTestAttribute : TestAttribute, IWrapSetUpTearDown
{
    public TestCommand Wrap(TestCommand command)
    {
        return new MyTestCommand(command, command.Test.Arguments);
    }
}
[TestCase(1)]
[TestCase(2)]
[MyTest]
public void MyTest(int foo)
{
//...
}