This project has moved. For the latest updates, please go here.
Consider the following unit tests:

[TestClass]
public class SimpleAsyncUnitTests
{
  // A simple test that should pass; it throws an exception from an async continuation.
  [TestMethod]
  [ExpectedException(typeof(InvalidOperationException))]
  public async void ShouldPass()
  {
    await Task.Yield(); // Change this to "TaskEx.Yield" on VS2010.
    throw new InvalidOperationException();
  }

  // A simple test that should fail; it fails an assertion from an async continuation.
  [TestMethod]
  public async void ShouldFail()
  {
    await Task.Yield(); // Change this to "TaskEx.Yield" on VS2010.
    Assert.Fail();
  }
}

If you run these tests, you'll get this result:

Passing tests that should fail - Failing tests that should pass!

That'll mess up your Red/Green/Refactor cycle!!!

Official Recommendation

The reason async unit tests don't work as expected is because they do not have a proper context.

The Async CTP team has put together a context suitable for unit testing; if you have the Async CTP installed, it is under My Documents\Microsoft Visual Studio Async CTP\Samples\(C# Testing) Unit Testing, and it is called GeneralThreadAffineContext .

After copying the source files from that directory into your test project, you can use it like this:

[TestClass]
public class SimpleAsyncUnitTests
{
  // A simple test that should pass; it throws an exception from an async continuation.
  [TestMethod]
  [ExpectedException(typeof(InvalidOperationException))]
  public void ShouldPass()
  {
    GeneralThreadAffineContext.Run(async () =>
    {
      await Task.Yield(); // Change this to "TaskEx.Yield" on VS2010.
      throw new InvalidOperationException();
    }
  }

  // A simple test that should fail; it fails an assertion from an async continuation.
  [TestMethod]
  public void ShouldFail()
  {
    GeneralThreadAffineContext.Run(async () =>
    {
      await Task.Yield(); // Change this to "TaskEx.Yield" on VS2010.
      Assert.Fail();
    }
  }
}

Now, each test has its own context. The tests are no longer async methods; rather, they set up the context and then run an async lambda within that context. The async lambda is the real test.

There Must Be a Better Way

Copy files into each of your test projects?

Change every asynchronous test method to contain its own context?

There must be a better way. And now there is!

Last edited Feb 2, 2012 at 3:04 AM by StephenCleary, version 6

Comments

No comments yet.