C# - Selenium - 重试属性不与Selenium超时一起使用

jou*_*aon 3 c# selenium nunit unit-testing

RetryAttribute从这篇文章中获取了以下自定义:NUnit重试动态属性.它工作正常但是当我在Selenium中出现超时错误时它无效.

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
            wait.Until(ExpectedConditions.ElementToBeClickable(element));
Run Code Online (Sandbox Code Playgroud)

重试自定义属性:

/// <summary>
/// RetryDynamicAttribute may be applied to test case in order
/// to run it multiple times based on app setting.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RetryDynamicAttribute : RetryAttribute {
    private const int DEFAULT_TRIES = 1;
    static Lazy<int> numberOfRetries = new Lazy<int>(() => {
        int count = 0;
        return int.TryParse(ConfigurationManager.AppSettings["retryTest"], out count) ? count : DEFAULT_TRIES;
    });

    public RetryDynamicAttribute() :
        base(numberOfRetries.Value) {
    }
}
Run Code Online (Sandbox Code Playgroud)

然后应用自定义属性.

[Test]
[RetryDynamic]
public void Test() {
    //.... 
}
Run Code Online (Sandbox Code Playgroud)

怎么解决这个问题?

Nko*_*osi 5

根据此处的文档

NUnit 文档重试属性

如果测试出现意外异常,则返回错误结果并且不会重试。只有断言失败才能触发重试。要将意外异常转换为断言失败,请参阅 ThrowsConstraint

强调我的。

相关的ThrowsNothingConstraint只是断言委托不会抛出异常。

您需要捕获异常并在未预期异常的情况下导致失败的断言。

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
Assert.That(() => {
         wait.Until(ExpectedConditions.ElementToBeClickable(element));
     }, Throws.Nothing);
Run Code Online (Sandbox Code Playgroud)

所以上面的代码只是说执行操作,它不应该期待异常。如果抛出异常,那么它是一个失败的断言。如果将属性应用于测试,则重试将执行。


Flo*_* B. 5

另一个解决方案是实现自己RetryAttribute的捕获WebDriver异常.这样您就不必改变测试:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RetryAttributeEx : PropertyAttribute, IWrapSetUpTearDown
{
    private int _count;

    public RetryAttributeEx(int count) : base(count) {
        _count = count;
    }

    public TestCommand Wrap(TestCommand command) {
        return new RetryCommand(command, _count);
    }

    public class RetryCommand : DelegatingTestCommand {

        private int _retryCount;

        public RetryCommand(TestCommand innerCommand, int retryCount)
            : base(innerCommand) {
            _retryCount = retryCount;
        }

        public override TestResult Execute(TestExecutionContext context) {

            for (int count = _retryCount; count-- > 0; ) {

                try {
                    context.CurrentResult = innerCommand.Execute(context);
                }
                catch (WebDriverTimeoutException ex) {
                    if (count == 0)
                      throw;

                    continue;
                }

                if (context.CurrentResult.ResultState.Status != ResultState.Failure.Status)
                    break;

                if (count > 0)
                    context.CurrentResult = context.CurrentTest.MakeTestResult();
            }

            return context.CurrentResult;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)