Thursday, 9 May 2013

MSTest and missing reference assemblies

Whilst attempting to convert our JIT build engine to use projects and not solutions I encountered a number of strange warnings and erros because we are now using MSBuild in a different way.

Tests were failing because of a missing assembly referenced by my test assembly.

There was no indication what assembly was missing, just that the test failed because of it its absence.

I stumbled upon this wonderful tool provided by Microsoft and it ships with either VS or the SDK (apparently).

Assembly Binding Log Viewer (Fuslogvw.exe)

The Assembly Binding Log Viewer displays details of failed assembly binding attempts. This information helps you diagnose why the .NET Framework cannot load an assembly at run time.

It's basically a very handy tool for finding out what assemblies are missing from your runtime environment. In my case, finding what assemblies MSTest hadn't deployed but my tests containers needed.

Configuring the log viewer is covered adequately in the MSDN guide to the Assembly Binding Log Viewer.

What I can offer however is how I used the tool to find those missing assemblies that failed my tests and how I was able to resolve them using this too.

Using Fuslogvw.exe to find MSTest assembly reference errors

With the tool running, I was now able to capture any binding errors being thrown up whilst running the Unit Tests.

As the MSDN article shows, the log viewer kicks out a lof of information, but crucially it includes which assemblies MSTest/QTAgent failed to find.

=== Pre-bind state information ===
LOG: User = *********
LOG: DisplayName = My.Test.Assembly.Container
(Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: My.Test.Assembly.Container | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///c:/temp/meee_L00002 2013-04-17 14_46_26/Out
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = QTAgent32.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: c:\temp\meee_L00002 2013-04-17 14_46_26\Out\My.UnitTest.Container.config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL ...
LOG: Attempting download of new URL ...
LOG: Attempting download of new URL ...
LOG: Attempting download of new URL ...
LOG: Attempting download of new URL ...
LOG: All probing URLs attempted and failed.

Using the deployment item attribute

The log viewer showed me which assembly was missing. And when I checked the directory where MSTest deploys all the assemblies needed for the test run, it was definitely missing. 

There is an attribute called the DeploymentItemAttribute which is for occasions such as this.

Decorate your test class with this attribute:
[DeploymentItem("My.Tests.Missing.Referenced.Assembly.dll")]

And the next time MSTest runs, it also includes this assembly in its deployment.

Mystery solved.