Async unit test

With the .NET framework 4.5 making stuff asynchronous has become very easy with async-await. I’ve looked into unittesting this new way of programming and found that Microsoft thought about this too.

During my exercise I had to replace my trusted RhinoMocks by NSubstitute when the tests are run in parallel. Seems that RhinoMocks is not thread safe. More about that in the references.

Object under test

For my demos I’ve created a business object called Garage that uses a data repository to save Cars.

public interface IRepository {
    Task<int> CreateCar(Car carToCreate);
}
public class Garage {
    private IRepository Repository { get; set; }
    public Garage(IRepository repository) {
        Repository = repository;
    }
    public async Task<int> AddCar(Car carToAdd) {
        // add some logic here to validate the car
        var newId = await Repository.CreateCar(carToAdd);
        return newId;
    }
}
public class Car { }

Three tier architecture.

Unit test

There are a few ways to mock the repository call in the AddCar method. I’ll try to show the different approaches in the code samples below.

[TestMethod]
public void Garage_addCar_returns_1_run() {
    var validCar = CreateValidCar();
    var fakeRepository = Substitute.For<IRepository>();
    fakeRepository
        .CreateCar(validCar)
        .Returns(Task.Run<int>(() => 1));
    var garage = CreateGarage(fakeRepository);

    var result = default(int);
    var task = garage.AddCar(validCar);
    task.Wait();
    result = task.Result;

    Assert.AreEqual(1, result);
}

This first code shows that the repository creates a task and runs it to return the result (1). Then after the call to the garage.AddCar we’ll have to wait for the task to complete and collect the result.

When CreateCar is called a Task is run. There is some overhead setting this up by the framework. For testing purposes use the Task.FromResult.

[TestMethod]
public void Garage_addCar_returns_2_fromresult() {
    var validCar = CreateValidCar();
    var fakeRepository = Substitute.For<IRepository>();
    fakeRepository
        .CreateCar(validCar)
        .Returns(Task.FromResult<int>(2));
    var garage = CreateGarage(fakeRepository);

    var result = default(int);
    var task = garage.AddCar(validCar);
    task.Wait();
    result = task.Result;

    Assert.AreEqual(2, result);
}

With Task.FromResult no actual thread is spawn, but the result is returned immediate as if it is from a task. We still have to wait for the AddCar to finish.

Async unit test

A unittest is a method. That method can be async as well. See the code below that correspondents with the first unittest that creates and runs an actual task.

[TestMethod]
public async Task Garage_addCar_returns_11_run() {
    var validCar = CreateValidCar();
    var fakeRepository = Substitute.For<IRepository>();
    fakeRepository
        .CreateCar(validCar)
        .Returns(Task.Run<int>(() => 11));
    var garage = CreateGarage(fakeRepository);

    var result = default(int);
    result = await garage.AddCar(validCar);

    Assert.AreEqual(11, result);
}

The method must return a Task for the MSTest framework to wait on and is marked async so the garaga.AddCar can be await.

We can improve the unittest by using the Task.FromResult.

[TestMethod]
public async Task Garage_addCar_returns_12_fromresult() {
    var validCar = CreateValidCar();
    var fakeRepository = Substitute.For<IRepository>();
    fakeRepository
        .CreateCar(validCar)
        .Returns(Task.FromResult<int>(12));
    var garage = CreateGarage(fakeRepository);

    var result = default(int);
    result = await garage.AddCar(validCar);

    Assert.AreEqual(12, result);
}

There is less clutter for handling the async method. Unittesting is all about Readable, Trustworthy, Fast and Maintainable 🙂

References

About erictummers

Working in a DevOps team is the best thing that happened to me. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Development and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.