Unit-Testing of Private Async Methods with Visual Studio

Description

Given there is the need to unit-test a private async method. The class to be tested might look like this:

public ClassWithPrivateAsyncMethod
{

// Some stuff

  private async Task PrivateMethodAsync
    (
    string input
    )
  {
    // Call some async framework method
    var result = await SomeFrameworkMethodAsync(input);
    // do something with the result
  }

// Some more stuff

}

In former – pre VS 2012 – times, one would have right-clicked inside this method, selected “Create Private Accessor…” and “Create Unit Tests…”, and implemented the test. Well, this was then, and this is now. Nowadays, Visual Studio does not have the ability to create private accessors and unit test bodies.

But still, you are able to create a unit test yourself. And instead of having a private accessor, we can use Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject, which is included in %Program Files (x86)%\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v4.0\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll (path might vary slightly with version changes, this is the path for Visual Studio 2012). To use this assembly, I had to set the Copy Local property of the reference to true.

Instead of wring much more prose, here is the implementation of the unit test method:

using Mut = Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
// some other usings

[TestClass]
public ClassWithPrivateAsyncMethodTest
{
  [TestMethod]
  public async Task TestPrivateMethodAsync()
  {
    // Create an instance of the class to be tested.
    ClassWithPrivateAsyncMethod instance 
      = new ClassWithPrivateAsyncMethod();

    // Create a PrivateObject to access private methods of instance.
    Mut.PrivateObject privateObject 
      = new Mut.PrivateObject(instance);

    // Create parameter values
    object[] args = new object[] { "SomeValue" };

    // Call the method to be tested via reflection, 
    // pass the parameter, and await the return
    await (Task)privateObject.Invoke("PrivateMethodAsync", args);

    // Do some validation stuff
  }

// Some more stuff

}

Have a look at the checklist to be sure not to miss a detail.

Checklist

Check Combine async with return type Task in method declaration. Even if the method returns void, the declaration should be async Task. Otherwise, it cannot be awaited, what would lead to some trouble in testing the method.

Check Add reference to Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll in your test project. You have to browse for it. The assembly can be found in %Program Files (x86)%\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v4.0\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll (path might vary slightly with version changes, this is the path for Visual Studio 2012).

Check Set Copy Local to true for the Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll reference.

Check Declare an alias for the Microsoft.VisualStudio.TestTools.UnitTesting namespace if you also use Microsoft.VisualStudio.TestPlatform.UnitTestFramework. This helps you to avoid using classes of the wrong namespace. There are some overlappings.

Check Declare testing method async Task, not async void. Otherwise, it will not be recognized as a test method, even when the TestMethodAttribute is set.

Check Use PrivateObject to access private methods of instances. It is located in the namespace Microsoft.VisualStudio.TestTools.UnitTesting.

Check Cast PrivateObject.Invoke‘s return value to Task to make sure the call can be awaited.

Check Await PrivateObject.Invoke. Otherwise, the tests in the testing method might be performed before the tested method returns.

Links

Async Return Types (C# and Visual Basic)

Handling of Out-Parameters with .NET Reflection

Leave a Comment

Please note that every comment requires the admin's approval before it will be published.

Any comment containing spam like "... great topic. Feel free to visit my web page ..." will be deleted. So please do not waste your and our time by leaving such comments.

We respect your privacy. When you leave a comment, your IP address will not be recorded by WordPress.