Bonus unit testing part 8: Testing async code

Search for a command to run...

No comments yet. Be the first to comment.
Phew, that was a lot. I'm always surprised how simple solutions contain so much detail and nuance when I try to explain them. I hope I conveyed the message that A-Frame simplifies code within a module or class by separating the infrastructure compone...

No architecture is complete without an easy way to test the functionality. My recommended strategy is to use two types of tests: unit and integration. As far as test setup goes, there is no clear winner for a test framework. xUnit.NET, NUnit and the ...

The examples in the previous posts seem really nice for simple scenarios. How do I approach more advanced use cases? The big scenarios are: I need multiple pieces of data from different sources I need to perform an infrastructure call in the middle...

I’ve shown how I write code using the A-Frame architecture without help. Yet I find a library or framework very convenient when using advanced techniques. Wolverine is at its core a messaging framework, but goes well beyond that. It has an in-memory ...

Now the expected structure is clear, let's take a look at the code. I will start with a minimal API implementation to demonstrate that there is no need for a framework to implement this architecture. I do know of a framework that makes A-Frame effort...

This series of posts accompanies a talk I give about unit testing with the xUnit test framework. Any reader who saw my talk can use this as a reference, others can use this as a starting point to write even better maintainable and reliable code. In my talk, I highlight patterns that work well with easily testable code and combine it all into a working application. All code from these articles can be found in a repository on my GitHub account.
After all the synchronous code I wrote, I wondered how xUnit handles asynchronous code. It is (unsurprisingly) very easy.
If I want to test asynchronous code, I first need an asynchronous method. I added a LaunchAsync() method in the Launcher class for this purpose.
public Task LaunchAsync()
{
return Task.Factory.StartNew(() =>
{
var missile = missiles.First();
missiles.Remove(missile);
});
}
Nothing groundbreaking, but it does what I need it to do. The method starts a task in the background, which will find the first missile from the missiles list and it removes it from said list.
Also note that I use the standard way of indicating that a function is asynchronous by adding 'Async' to the name. This is not necessary, but it does follow clean naming conventions, a very important practice in TDD.
Adding a test that is aware of the fact that this code is executed asynchronous is quite easy. All I have to do is change the signature from public void MethodName() to public async Task MethodName() and I'm done. xUnit supports async test methods out of the box. Now I can use the await keyword next to asynchronous functions and await the result.
[Fact]
public async Task When_launching_missile_then_decrease_missile_count_by_1()
{
// arrange
var launcher = new Launcher(new List<IMissile> { mockMissile.Object });
// act
await launcher.LaunchAsync();
// assert
Assert.Equal(0, launcher.MissileCount);
Assert.True(launcher.MissileCount == 0, "Missile was not launched");
}
I rebuild the solution so the new test is discovered and I'm able to execute the tests as normal.
Adding asynchronous data driven tests is just as easy. Fetching the data is handled by the xUnit framework and then the test is run asynchronously with the data passed to it. It works just like non data driven tests as described previously. Below is an example of a data driven test that is loading data via the MemberData attribute.
public static IEnumerable<object[]> MissileData => new[]
{
new object[] {new HeatSeekingMissile()},
new object[] {new LaserGuidedMissile()}
};
[Theory]
[MemberData(nameof(MissileData))]
public async Task When_launching_missile_from_memberdata_then_decrease_missile_count_by_1Async(IMissile missile)
{
// arrange
var launcher = new Launcher(new List<IMissile> { missile });
// act
await launcher.LaunchAsync();
// assert
Assert.Equal(0, launcher.MissileCount);
}
Again, all I have to do is rebuild the solution and execute the tests, xUnit takes care of the rest.
Although xUnit makes it easy to write tests for asynchronous code, it does not mean it's exempt from race conditions, deadlocks and other asynchronous pitfalls. It is the developers responsibility to make sure that such problems do not occur within the tests they write.
It's is very easy to test asynchronous code with xUnit, so nobody has any excuses to leave some code untested. If anybody notices any inconsistencies, please contact me on twitter or via email.