Here's a workaround that is working for me. It was a bit tricky to figure out, but finally all unit tests against my .NET 4.0 libraries are being detected and showing up in Test Explorer, running and passing, and they're all written as normal async Task methods, without any special test runners, wrappers or third-party dependencies.
- Change the target Framework of your unit test project to .NET 4.5.
- Yes, you must do this even if the project references that you're testing target .NET 4.0.
- Remove the Microsoft.Bcl, Microsoft.Bcl.Build and Microsoft.Bcl.Async NuGet package references from your unit test project. If you haven't added these references, then simply do not add them to your unit test project.
- Add System.Runtime.dll and System.Threading.Tasks.dll to your unit test project as linked files in the project's root directory.
- Right-mouse click your unit test project in Solution Explorer.
- Add > Existing Item...
- Browse to your solution's packages folder and locate the net40 package folder for Microsoft.Bcl; e.g., ...\packages\Microsoft.Bcl.1.1.10\lib\net40\
- Select All Files (*.*) in the file type drop down.
- Holding the Ctrl key, left-mouse click System.Runtime.dll and System.Threading.Tasks.dll to select them.
- Click the small drop-down arrow on the Add button. (Do not click the Add button.)
- In the Add button's drop-down, click Add As Link. Both assemblies are now visible at the root of your project.
- You must leave the assembly links at the root of your project. Do not move them to a subfolder.
- If your project is under source control, then you may notice that these linked files are marked as excluded (and if they aren't, they should be.) The NuGet packages folder, where these files reside, should not be checked into source control. Since they are merely linked files, anyone pulling down your changes should have no problems at all after restoring their NuGet packages.
- Select both of the linked assembly files in Solution Explorer (Ctrl + Left click) or simply perform the following steps on each file separately.
- Right-mouse click either of the selected files and select Properties. The Properties window opens.
- Set the Copy to Output Directory field to Copy if newer.
Your unit test project file should now contain something similar to the following.
<ItemGroup>
<Content Include="..\..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll">
<Link>System.Runtime.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll">
<Link>System.Threading.Tasks.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
And that's it!
Just keep in mind that your unit test project targets .NET 4.5 (or a higher version, if you'd like) and so unit tests can use async methods and any other .NET 4.5 features. There shouldn't be any conflicts with the .NET 4.0 assemblies that you're testing, but if you do find conflicts, it's probably because you've redefined some types for newer Framework/C# features and made them public, thus causing conflicts when you try to use those same types in your unit tests. The best solution is to simply make those types internal to the projects you're testing.
Edit:
After following those steps, you may get some build warnings:
All projects referencing My.csproj must install nuget package Microsoft.Bcl.Build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317569
{root}\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets
To avoid these warnings, simply edit the unit test project and add the following metadata element to each project reference that points to a project that references Microsoft.Bcl.Build.
<Properties>SkipValidatePackageReferences=true</Properties>
For example:
<ProjectReference Include="..\pcl\pcl.csproj">
<Project>{664a9e98-fac7-4567-a046-0dde95fddb48}</Project>
<Name>pcl</Name>
<Properties>SkipValidatePackageReferences=true</Properties>
</ProjectReference>
The full explanation can be found in the noted .targets file included with the Microsoft.Bcl.Build package. Here's the full comment, for your convenience.
BclBuildValidateNugetPackageReferences
This target validates that any Nuget packages installed in the current project are also installed in projects
referencing the current project.
This is necessary because Nuget packages contain more than just simple references. Installing the package ensures
1. The right set of references for the target framework are added
2. Config file transforms are applied
3. Project installation scripts are run
For all packages listed as installed for the current project in packages config, if the package ID matches one specified in @(ValidatePackages), ensure that the same package is installed in the referencing project.
This target can be disabled for a project reference by setting SkipValidatePackageReferences=true for the reference:
<ProjectReference Include="..\pcl\pcl.csproj">
<Project>{664a9e98-fac7-4567-a046-0dde95fddb48}</Project>
<Name>pcl</Name>
<Properties>SkipValidatePackageReferences=true</Properties>
</ProjectReference>
This target can be disabled for all references to a project by adding the following:
<PropertyGroup>
<SkipValidatePackageReferences>true</SkipValidatePackageReferences>
</PropertyGroup>
[TestMethod]. Why do you think there is a better way for VS2010, other than Stephen's solution you mentioned? With VS2012+ (and thus with TFS2012+), there's built-in support for this. - noseratioasynckeyword doesn't change the signature of the method, it's just a method returning aTask, either in .NET 4.5 or .NET 4.0. What's the .NET target version in your Unit Test Project? - noseratio