【问题标题】:How to retry a NUnit TestCase?如何重试 NUnit 测试用例?
【发布时间】:2020-02-19 13:49:49
【问题描述】:

我想实现一个设置,我可以为每个测试设置所需的重试次数,例如,我可以在实际失败之前重试所有失败的测试。我以这种方式构建了我的测试:

    [TestCase(“Some parameter”, Category = “Test category”, TestName = “Name of test”, Description = “Description of test”)]
    public void SomeTestName(string browser) {
    //Test script
    }

如果我使用 [Test] 而不是 [TestCase] 我可以添加一个 [Retry(1)] 属性,但是如何使用 [TestCase] 实现相同的行为?我已经看过NUnit retry dynamic attribute,它有一个非常简洁的解决方案,但不幸的是,当我尝试将它应用于 [TestCase] 时它没有任何效果

【问题讨论】:

  • 这没有意义,如果一个测试失败了,你为什么认为如果你再次运行它会起作用?
  • 这很有意义。例如,如果您使用包含 200 个 selenium 测试的套件来确定发布是否应该继续或回滚,您不希望一个测试用例中的缓慢加载字体导致回滚。是的,您可以主张提高测试稳定性,但如果应用程序本身不够稳定,那就没有意义了……@DavidG
  • 值得一提的是,只有断言失败才会触发重试。这在documentation 中进行了讨论

标签: c# nunit


【解决方案1】:

根据文档:“RetryAttribute 用于测试方法,以指定如果失败则应重新运行,最多可重复运行次数。”

也就是说,参数不是您可能认为的重试次数,而是运行测试的总尝试次数,[Retry(1)] 根本没有影响,无论您在哪里使用它。由于这可能会造成混淆,因此我只是编辑了该页面以给出明确的警告。

如果你试图在一个类上使用RetryAttribute,你会得到一个编译器警告,因为它只能用在方法上。但是,在 NUnit 中,一个方法可以表示单个测试或一组参数化测试。在参数化测试的情况下,该属性当前无效。

NUnit 团队可以决定该属性适用于每个单独的测试用例并相应地修改 nunit。 TestCaseAttribute 也可以采用一个可选参数来指定重试次数。对于长期解决方案,您可能需要向他们询问这些选项中的一个或另一个。

在短期内,作为一种解决方法,您可以考虑从TestCaseAttribute 派生您自己的属性。这里有一些(未经测试的)代码可以帮助您入门...

using System;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal.Commands;

namespace NUnit.Framework
{
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
  public class RetryTestCaseAttribute : TestCaseAttribute, IRepeatTest
  {
    // You may not need all these constructors, but use at least the first two
    public RetryTestCaseAttribute(params object[] arguments) : base(arguments) { }
    public RetryTestCaseAttribute(object arg) : base(arg) { }
    public RetryTestCaseAttribute(object arg1, object arg2) : base(arg1, arg2) { }
    public RetryTestCaseAttribute(object arg1, object arg2, object arg3) : base(arg1, arg2, arg3) { }

    public int MaxTries { get; set; }

    // Should work, because NUnit only calls through the interface
    // Otherwise, you would delegate to a `new` non-interface `Wrap` method.
    TestCommand ICommandWrapper.Wrap(TestCommand command)
    {
      return new RetryAttribute.RetryCommand(command, MaxTries);
    }
  }
}

你可以像下面这样使用它

[RetryTestCase("some parameter", MaxTries=3)]
public void SomeTestName(string browser)
{
  // Your test code
}

关于上述的一些注意事项:

  1. 我已编译此代码,但尚未对其进行测试。如果您尝试过,请发表评论,尤其是在需要修改时。

  2. 代码依赖于 NUnit 内部的一些知识,将来可能会中断。需要更全面的实施以使其经得起未来的考验。特别是,我使用了IRepeatTest 基于ICommandWrapper 但没有添加任何方法的事实。我相信这两个接口中的每一个都需要我放置它们的位置,因为 NUnit 在其代码中的不同点检查它们。

  3. 与将重试计数添加到TestCaseAttribute 所需的代码行数相比,此代码的代码行数大约是其三倍!如果您想要该功能,请咨询 NUnit 项目 - 或自己贡献!

【讨论】:

  • 谢谢!到目前为止,这就像一个魅力。如果遇到任何问题,我会进一步评估解决方案并及时通知您。
  • 解决方案很好,但在一种方法上与多个测试用例属性一起使用时,会显示烦人的错误消息“重复测试的多个属性可能会导致问题。”。另一件事是,它看起来像多次重复测试,而不是按规定。
  • @Peter Minko 是的,这是 NUnit 现在进行的内部检查。因此,该解决方法仅适用于具有单个 TestCase 属性的方法。
【解决方案2】:

这里的任何人都希望看到 @Charlie 的 TestCaseSource 解决方案的调整版本。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
    public class RetryTestCaseSourceAttribute : TestCaseSourceAttribute, IRepeatTest
    {
        #region constructors
        public RetryTestCaseSourceAttribute(string sourceName) : base(sourceName){}
        public RetryTestCaseSourceAttribute(Type sourceType) : base(sourceType){}
        public RetryTestCaseSourceAttribute(Type sourceType, string sourceName) : base(sourceType, sourceName){}
        public RetryTestCaseSourceAttribute(string sourceName, object[] methodParams) : base(sourceName, methodParams){}
        public RetryTestCaseSourceAttribute(Type sourceType, string sourceName, object[] methodParams) : base(sourceType, sourceName, methodParams){}
        #endregion

        #region repeat components
        public int MaxTries { get; set; }
        TestCommand ICommandWrapper.Wrap(TestCommand command) => new RetryAttribute.RetryCommand(command, MaxTries);
        #endregion
    }

然后您可以使用:

[RetryTestCaseSource(nameof(GetBrowsers), MaxTries = 3)]
public void SomeTestName(string browser)
{
    // Your test code
}
public static IEnumerable<string> GetBrowsers()
{
    return new List<string>() {/* browsers */};
}        

此解决方案已针对 NUnit 3.12.0 进行了验证。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-30
    相关资源
    最近更新 更多