Last active
August 27, 2015 19:47
-
-
Save di97mni/f105ca512bbaafa249ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[NUnitAddin(Type = ExtensionType.Core, Description = "Retries an intermittently failing test.")] | |
public class RetryTestAddin : IAddin, ITestDecorator | |
{ | |
public bool Install(IExtensionHost host) | |
{ | |
var testDecorators = host.GetExtensionPoint("TestDecorators"); | |
if (testDecorators == null) return false; | |
testDecorators.Install(this); | |
return true; | |
} | |
public Test Decorate(Test test, MemberInfo member) | |
{ | |
var retryAttr = GetRetryAttribute(member, test); | |
if (retryAttr == null) return test; | |
if (test != null && | |
test.GetType() == typeof(NUnitTestMethod) && | |
((NUnitTestMethod)test).Method. | |
GetCustomAttributes(typeof(IgnoreAttribute), true). | |
Length == 0) | |
{ | |
return new RetriedTestMethod( | |
(NUnitTestMethod)test, | |
retryAttr.Times, | |
retryAttr.RequiredPassCount); | |
} | |
if (!(test is ParameterizedMethodSuite)) return test; | |
var testMethodSuite = (ParameterizedMethodSuite)test; | |
System.Collections.IList newTests = new List<Test>(); | |
foreach (Test childTest in testMethodSuite.Tests) | |
{ | |
if (childTest != null && | |
childTest.GetType() == typeof(NUnitTestMethod) && | |
((NUnitTestMethod)childTest).Method. | |
GetCustomAttributes(typeof(IgnoreAttribute), true). | |
Length == 0) | |
{ | |
var oldTest = (NUnitTestMethod)childTest; | |
var newTest = new RetriedTestMethod( | |
oldTest, | |
retryAttr.Times, | |
retryAttr.RequiredPassCount); | |
newTests.Add(newTest); | |
} | |
else | |
{ | |
newTests.Add(childTest); | |
} | |
} | |
testMethodSuite.Tests.Clear(); | |
foreach (Test newTest in newTests) | |
{ | |
testMethodSuite.Add(newTest); | |
} | |
return testMethodSuite; | |
} | |
private static RetryAttribute GetRetryAttribute(MemberInfo member, Test testMethodSuite) | |
{ | |
var retryAttr = GetRetryAttribute(member); | |
if (retryAttr == null && testMethodSuite.FixtureType != null) | |
{ | |
retryAttr = GetRetryAttribute(testMethodSuite.FixtureType); | |
} | |
return retryAttr; | |
} | |
private static RetryAttribute GetRetryAttribute(MemberInfo member) | |
{ | |
var attrs = member.GetCustomAttributes(typeof(RetryAttribute), true) | |
.Cast<RetryAttribute>(); | |
return attrs.FirstOrDefault(); | |
} | |
} | |
public class RetriedTestMethod : NUnitTestMethod | |
{ | |
readonly int _requiredPassCount; | |
readonly int _tryCount; | |
public RetriedTestMethod(NUnitTestMethod test, int tryCount, int requiredPassCount) | |
: base(test.Method) | |
{ | |
_tryCount = tryCount; | |
_requiredPassCount = requiredPassCount; | |
} | |
public override TestResult Run(EventListener listener, ITestFilter filter) | |
{ | |
var successCount = 0; | |
TestResult failureResult = null; | |
for (var i = 0; i < _tryCount; i++) | |
{ | |
if (i > 0) | |
{ | |
Console.WriteLine(string.Format("ailed Retrying Test Case now...xRETRYx.. [{0}] of [{1}]", i + 1, _tryCount)); | |
} | |
if (i > 1) | |
{ | |
Console.WriteLine(string.Format("Pausing for retry for {0} seconds", i)); | |
Thread.Sleep(i*2000); // increase the pause on each run (reason to allow CPU/db, etc to die off and start the tests fresh.) | |
} | |
var result = base.Run(listener, filter); | |
if (!TestFailed(result)) | |
{ | |
if (i == 0) | |
{ | |
return result; | |
} | |
if (++successCount >= _requiredPassCount) | |
{ | |
return result; | |
} | |
} | |
else | |
{ | |
failureResult = result; | |
} | |
} | |
return failureResult; | |
} | |
private static bool TestFailed(TestResult result) | |
{ | |
return result.ResultState == ResultState.Error || result.ResultState == ResultState.Failure; | |
} | |
} | |
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] | |
public class RetryAttribute : Attribute | |
{ | |
public RetryAttribute(int times = 5, int requiredPassCount = 1) | |
{ | |
if (requiredPassCount > times) | |
{ | |
throw new Exception("Required Pass Count must be lower or equal than the number of retries."); | |
} | |
Times = times; | |
RequiredPassCount = requiredPassCount; | |
} | |
public int RequiredPassCount { get; set; } | |
public int Times { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment