NUnit 扩展 ICommandWrapper 如何包装 TestCase?

NUnit extending ICommandWrapper How do I wrap a TestCase?

我尝试扩展扩展 ICommandWrapper,关注这篇文章:https://www.skyrise.tech/blog/tech/extending-nunit-3-with-command-wrappers/。我发现我也可以扩展 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 的代码包装。

[编辑] 示例:
假设 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)

您的问题的关键在于... C# 允许您使用属性修饰方法或 class。但是在 NUnit 之外不存在单独的测试用例 - 没有 C# 等价物 - 所以你不能装饰它。

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

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

希望这能解释正在发生的事情。

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

想通了。

我可以扩展 TestAttribute 并使用 command.Test.Arguments 从标准 TestCaseAttribute 获取要传递给包装器 class 的参数,而不是扩展 TestCaseAttribute .

[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)
{
//...
}