I’m refactoring some code and creating unit tests. However I’m unable to verify Func was called the right amount of times. I renamed and removed some things here and there since I can’t share that online.
The retry logic which has unit tests works as expected. Now I’m trying to test some code which uses the retry logic.
public async Task<bool> ExecuteWithRetriesAsync(Func<Task> action, int maxRetryCount = 3, int delayInSeconds = 5)
{
var attempted = 0;
while (attempted < maxRetryCount)
{
try
{
await action();
return true;
}
catch (DataServiceClientException ex) when (ex.StatusCode == 429)
{
attempted++;
var randomRetryDelay = _delayProvider.Generate(delayInSeconds, 2);
await Task.Delay(TimeSpan.FromSeconds(randomRetryDelay));
delayInSeconds += 5;
}
}
return false;
}
We have the following code which uses this retry logic in case of an exception.
public async Task TryDeleteAsync(ToDelete toDelete)
{
try
{
// some logic before we save
await _someClient.SaveChangesAsync();
}
catch (DataServiceClientException ex)
{
var deletedSuccessfully = await _retryHandler.ExecuteWithRetriesAsync(async () => await _someClient.SaveChangesAsync());
if (deletedSuccessfully)
{
return;
}
throw new Exception("Retrying failed after 3 times");
}
}
Now I have this test with all mocks needed but the func only gets invoked twice…
[Fact]
public async Task TryDeleteAsync_AllRetriesFail_ShouldThrow()
{
var something = MockDataCreator.CreateSomething();
var something2 = MockDataCreator.CreateSomething(something);
_retryHandlerMock
.Setup(x => x.ExecuteWithRetriesAsync(It.IsAny<Func<Task>>(), 3, 5))
.Callback<Func<Task>, int, int>(async (action, _, _) =>
{
await action();
})
.ReturnsAsync(false); // returns false because all retries failed
_someClientMock
.SetupSequence(x => x.SaveChangesAsync())
.Throws(new DataServiceClientException(429)) // First try fails
.Throws(new DataServiceClientException(429)) // Second try fails
.Throws(new DataServiceClientException(429)) // Third try fails
.Throws(new DataServiceClientException(429)); // Fourth try fails
await Assert.ThrowsAsync<Exception>(
async () => await _sut.TryDeleteAsync(something2));
_someClientMock.Verify(x => x.SaveChangesAsync(), Times.Exactly(4)); // Assertion fails because it's only called twice
_retryHandlerMock.Verify(x => x.ExecuteWithRetriesAsync(It.IsAny<Func<Task>>(), 3, 5), Times.Once);
}
Who knows what to do in order to verify this in unit tests? Because actual code seems to work….