简体   繁体   中英

Testing property set by async method

I try to test a class with NUnit that contains async methods. I don't know how to do it in a correct way.

I have a class with that looks like this:

public class EditorViewModel:INotifyPropertyChanged
{
    public void SetIdentifier(string identifier)
    {
         CalcContentAsync();
    }

    private async void CalcContentAsync()
    {
         await SetContentAsync();
         DoSomething();
    } 

    private async Task SetContentAsync()
    {
        Content = await Task.Run<object>(() => CalculateContent());
        RaisePropertyChanged("Content");
    }

    public object Content { get; private set; }

    ...
}

How can I write a Test in NUnit, that checks, that the Content-Property is set to the right value? I want to do something like that:

[Test]
public void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
    var viewModel = new EditorViewModel();

    viewModel.SetIdentifier("XXX");

    Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}

But that doesn't work. Because the asynchronous code has not been executed before the assertion.

Your SetIdentifier method is async too (or you need to make it async because you wait operation inside it. Then your method can looks like next one:

public async Task SetIdentifier(string identifier)
{
     await SetContentAsync();
     DoSomething();
}

And now you can just await it in your unit test:

[Test]
public async Task Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
    var viewModel = new EditorViewModel();

    await viewModel.SetIdentifier("XXX");

    Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}

You can also use workaround to call your test in a sync manner:

[Test]
public async Task Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
    Task.Run(async () =>
    {
        var viewModel = new EditorViewModel();

        await viewModel.SetIdentifier("XXX");

        Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
    }).GetAwaiter().GetResult();
}

Via MSDN Magazine .

When working with async you should always return a Task. Otherwise it would be "fire and forget", and you have absolutely no way of interacting with the Task .

Therefore you should change the signature of SetIdentifier to return a Task , like this:

public async Task SetIdentifier(string identifier)
{
     await SetContentAsync();
     DoSomething();
}

Then you can wait for the operation to complete in the test:

[Test]
public async void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
    var viewModel = new EditorViewModel();

    await viewModel.SetIdentifier("XXX");

    Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}

Or, if your test runner does not support async :

[Test]
public void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
    var viewModel = new EditorViewModel();

    viewModel.SetIdentifier("XXX").Wait();

    Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM