I am working on extending the TestMethod
attribute in .NET Core. I am using Polly library for retry logic along with an outer timeout policy.
I want a helper method that can retry invoking the ITestMethod
until it passes. I don't mind the number of times it'll be retried. But I'll be putting a timeout within which it has to be completed. If the delegate is executed successfully within timeout, it's fine. But if there is a timeout exception, I still want the failed result value (the result of the last iteration) instead of TimeOutRejectedException
or the default value of the return type.
Below is my extended test method attribute class:
public sealed class GuaranteedPassTestMethodAttribute : TestMethodAttribute
{
/// <inheritdoc/>
public override TestResult[] Execute(ITestMethod testMethod)
{
return ExecuteTestTillSuccess(testMethod);
}
private TestResult[] ExecuteTestTillSuccess(ITestMethod testMethod)
{
var gracefulTestRun =
TestExecutionHelper.ExecuteTestTillPassAsync(
() => TestInvokeDelegate(testMethod));
return gracefulTestRun.Result;
}
private Task<TestResult[]> TestInvokeDelegate(ITestMethod testMethod)
{
TestResult[] result = null;
var thread = new Thread(() => result = Invoke(testMethod));
thread.Start();
thread.Join();
return Task.FromResult(result);
}
}
Below is my TestExecutionHelper
that uses Polly:
internal static class TestExecutionHelper
{
private static readonly Func<TestResult[], bool> TestFailurePredicate =
results => results != null &&
results.Length == 1 &&
results.First().Outcome != UnitTestOutcome.Passed;
internal static async Task<TestResult[]> ExecuteTestTillPassAsync(
Func<Task<TestResult[]>> testInvokeDelegate,
int delayBetweenExecutionInMs = 3000,
int timeoutInSeconds = 60 * 10)
{
var timeoutPolicy = Policy.TimeoutAsync<TestResult[]>(timeoutInSeconds);
var retryPolicy = Policy.HandleResult<TestResult[]>(TestFailurePredicate)
.WaitAndRetryAsync(int.MaxValue, x => TimeSpan.FromMilliseconds(delayBetweenExecutionInMs));
var testRunPolicy = timeoutPolicy.WrapAsync(retryPolicy);
return await testRunPolicy.ExecuteAsync(testInvokeDelegate);
}
}
With this setup, I either get a passed test method execution or TimeOutRejectedException
for failed tests. I want to capture failed test's TestResult
even after retries.