Shinbo
Shinbo

Reputation: 143

How to test an async void method

Please consider the code as shown below. By calling GetBrands, property Brands will be assigned with proper data.

public class BrandsViewModel : ViewModelBase
{
    private IEnumerable<Brand> _brands;
    public IEnumerable<Brand> Brands
    {
        get { return _brands; }
        set { SetProperty(ref _brands, value); }
    }

    public async void GetBrands()
    {
        // ......

        Brands = await _dataHelper.GetFavoriteBrands();

        // ......
    }
}

But if I test it as shown below, the test failed. How do I wait for the async call inside method GetBrands?

[TestMethod]
public void AllBrandsTest()
{
    BrandsViewModel viewModel = new BrandsViewModel();
    viewModel.GetBrands();
    Assert.IsTrue(viewModel.Brands.Any());
}

Upvotes: 4

Views: 2650

Answers (2)

CodeCaster
CodeCaster

Reputation: 151594

Your model (a DTO) is populating itself (data access). This is too much for one class to do. Usually when you ask yourself "How on earth can I test this", it's time for refactoring. Create a separate data access class:

BrandsViewModel viewModel = new BrandsViewModel();
var brandAccess = new BrandsDataAccess();
viewModel.Brands = await brandAccess.GetAllBrands();
Assert.IsTrue(viewModel.Brands.Any());

Now you can test BrandsDataAccess.GetAllBrands().

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1062780

The simple answer here is: don't make it an async void. In fact, don't ever make something an async void unless it absolutely has to be to work as an event-handler. The things that async void loses are precisely the things that you want here for your test (and presumably for your real code).

Make it an async Task method instead, and you now have the ability to wait for completion (with timeout) / add a continuation, and to check whether it exited with success or an exception.

This is a single word change, to:

public async Task GetBrands()
{
    // ......

    Brands = await _dataHelper.GetFavoriteBrands();

    // ......
}

and then in the test:

[TestMethod]
public async Task AllBrandsTest()
{
    BrandsViewModel viewModel = new BrandsViewModel();
    var task = viewModel.GetBrands();
    Assert.IsTrue(task.Wait(YOUR_TIMEOUT), "failed to load in time");
    Assert.IsTrue(viewModel.Brands.Any(), "no brands");
}

Upvotes: 9

Related Questions