Skip to content

Instantly share code, notes, and snippets.

@davidwhitney
Created April 14, 2015 12:41
Show Gist options
  • Save davidwhitney/5fb59a6cfcebb565afac to your computer and use it in GitHub Desktop.
Save davidwhitney/5fb59a6cfcebb565afac to your computer and use it in GitHub Desktop.
Retry example
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace ClassLibrary1
{
[TestFixture]
public class Class1
{
[Test]
public void Try_GivenPassingThing_CompletesSuccessfully()
{
var fakeHttp = new FakeHttp("data here");
var output = Try.To(() => fakeHttp.GetHttpData()).Now();
Assert.That(output, Is.EqualTo("data here"));
}
[Test]
public void Try_GivenFailingCall_RetriesCall()
{
var fakeHttp = new FakeHttp("data here", failCount: 1);
var tryWrapper = Try.To(() => fakeHttp.GetHttpData());
var output = tryWrapper.Now();
Assert.That(tryWrapper.Failures, Is.EqualTo(1));
Assert.That(output, Is.EqualTo("data here"));
}
[Test]
public void Try_GivenFailingCall_DoesNotTryForever()
{
var tryWrapper = Try.To(() =>
{
throw new Exception("Haha, always throws!");
return "hi";
});
Assert.Catch(() => tryWrapper.MaxAttempts(10).Now());
Assert.That(tryWrapper.Failures, Is.EqualTo(10));
}
[Test]
public void Try_NoMaxAttemptSet_DefaultsTo100Attempts()
{
var tryWrapper = Try.To(() =>
{
throw new Exception("Haha, always throws!");
return "hi";
});
Assert.Catch(() => tryWrapper.Now());
Assert.That(tryWrapper.Failures, Is.EqualTo(100));
}
[Test]
public void Try_NeverWorks_ThrowsAggregateException()
{
var tryWrapper = Try.To(() =>
{
throw new Exception("Haha, always throws!");
return "hi";
});
Assert.Throws<AggregateException>(() => tryWrapper.Now());
Assert.That(tryWrapper.Failures, Is.EqualTo(100));
}
[TestCase(1, 5)]
[TestCase(2, 10)]
[TestCase(3, 20)]
public void Try_FailsOnce_DelayedBy5Seconds(int failCount, int delaySeconds)
{
var fakeHttp = new FakeHttp("data here", failCount: failCount);
var tryWrapper = Try.To(() => fakeHttp.GetHttpData());
var output = tryWrapper.Now();
Assert.That(tryWrapper.Delays.Count, Is.EqualTo(failCount));
Assert.That(tryWrapper.Delays.Last(), Is.EqualTo(new TimeSpan(0, 0, 0, delaySeconds)));
}
[Test]
public void Try_KnownException_ThrowsRightAway()
{
var tryWrapper = Try.To(() =>
{
throw new ArgumentException("Haha, always throws!");
return "hi";
});
tryWrapper.Throw<ArgumentException>();
var ex = Assert.Throws<ArgumentException>(() => tryWrapper.Now());
Assert.That(ex.Message, Is.EqualTo("Haha, always throws!"));
Assert.That(tryWrapper.Failures, Is.EqualTo(0));
}
[Test]
public void ConfigExample()
{
var value = Try.To(() =>
{
return "hi";
})
.Throw<InvalidCastException>()
.Throw<ArgumentException>()
.MaxAttempts(20)
.Now();
Assert.That(value, Is.EqualTo("hi"));
}
}
public static class Try
{
public static MethodRunner<TReturnType> To<TReturnType>(Func<TReturnType> methodToExecute)
{
return new MethodRunner<TReturnType>(methodToExecute);
}
}
public class MethodRunner<TReturnType>
{
public List<TimeSpan> Delays { get; private set; }
public int Failures { get { return _errors.Count; } }
private readonly List<Exception> _errors;
private readonly List<Type> _throwAll;
private readonly Func<TReturnType> _methodToExecute;
private int _maxAttempts = 100;
private double _lastDelaySeconds = 2.5;
public MethodRunner(Func<TReturnType> methodToExecute)
{
_methodToExecute = methodToExecute;
_errors = new List<Exception>();
Delays = new List<TimeSpan>();
_throwAll = new List<Type>();
}
public MethodRunner<TReturnType> MaxAttempts(int maxAttempts)
{
_maxAttempts = maxAttempts;
return this;
}
public TReturnType Now()
{
while (true)
{
try
{
return _methodToExecute();
}
catch (Exception ex)
{
if (_throwAll.Contains(ex.GetType()))
{
throw;
}
_errors.Add(ex);
if (Failures >= _maxAttempts)
{
break;
}
Delay();
}
}
throw new AggregateException(_errors);
}
private void Delay()
{
_lastDelaySeconds = _lastDelaySeconds * 2;
var delay = new TimeSpan(0, 0, 0, (int)_lastDelaySeconds);
Delays.Add(delay);
//Thread.Sleep(delay);
}
public MethodRunner<TReturnType> Throw<T>()
{
_throwAll.Add(typeof (T));
}
}
public class FakeHttp
{
private readonly string _dataHere;
private readonly int _failCount;
private int _attempts = 0;
public FakeHttp(string dataHere, int failCount = 0)
{
_dataHere = dataHere;
_failCount = failCount;
}
public string GetHttpData()
{
if (_attempts < _failCount)
{
_attempts++;
throw new Exception("Fake failure");
}
return _dataHere;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment