3

I need to make sure a task is finished before moving on with the rest of my unit test.

But it is a task that is awaited inside another method (I am unit testing the method.)

I have tried awaiting the task or calling task.Wait() but each time the test does not work.

The actual method looks like this:

private async void DoStuff(long idToLookUp)
{
    IOrder order = await orderService.LookUpIdAsync(idToLookUp);   

    OtherStuff = false;
}    

I am trying to unit test it like this:

[TestMethod]
public void TestDoStuff()
{
    //+ Arrange
    var lookupTask = Task<IOrderableTest>.Factory.StartNew(() => validOrder);
    orderService.LookUpIdAsync(Arg.Any<long>()).Returns(lookupTask);

    //+ Act
    myViewModel.DoStuff();
    await lookupTask;

    //+ Assert
    myViewModel.OtherStuff.Should().BeFalse();
}

The way it fails is very unhelpful. In It causes another test to fail with "The agent process was stopped while the test was running." (From what I can read, that means a background thread threw an exception while the test was running.)

So I am wondering how I can get my code to wait for that unit test to finish.

I saw this answer that seems fairly good. But it is more geared toward the calling of the method. I am not calling it, I just have the task only.

NOTE: I am targeting .NET 4.0 using the ansyc pack.

Community
  • 1
  • 1
Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • 1
    Don't create `async void`s; you can't wait for them. – SLaks Jan 10 '13 at 22:00
  • @SLaks - Alas, many things require void method signatures. (Commands are one of them. I suppose I can have two methods for each of my command calls, but it seems messy.) – Vaccano Jan 10 '13 at 22:32
  • 1
    @Vaccano In such cases, you should be extremely careful. Because most of the time that means `async` just won't work with those things. Very rarely is using `async void` the right solution. – svick Jan 10 '13 at 22:43

2 Answers2

6

You should never have a void returning async method unless it's going to be an event handler (in which case you have no choice). When an async method is void you have no way of knowing when it finishes.

The method should return a Task (and not a Task<T> if it has no meaningful result, just an indication that it finishes).

Once it returns a Task you can await it when testing it, or use Wait, if that's appropriate.

You're awaiting one of the tasks the method is dependent on, but that doesn't mean it's finished it's own code. This means your test has introduced a race condition.

Servy
  • 202,030
  • 26
  • 332
  • 449
0

This call await myViewModel.DoStuff(); returns as soon as the await keyword is hit (well unless it is finished very quickly). Plus as Slaks mentionned you can't await a void return method.

Then you Wait on the var lookupTask = Task<IOrderableTest>.Factory.StartNew(() => validOrder); which doesn't seem to be doing much, except returning an IOrderableTest .

Plus I don't think you should do await lookupTask, this will again return to the caller, in this case the TestRunner doesn't/might not expect this.

You should use instead lookupTask.Wait() (this is a blocking call); And make sure it takes enough time for the myViewModel.DoStuff(); to be completed as well.

Summary :

private async Task DoStuff(long idToLookUp)
{
    IOrder order = await orderService.LookUpIdAsync(idToLookUp);   

    OtherStuff = false;
    return;
}

....

 var task = myViewModel.DoStuff();
 Task.WaitAll(task, lookupTask);
MBen
  • 3,956
  • 21
  • 25