Time Tortoise: Resolving Dependencies Part 2

This is one in a series of articles about Time Tortoise, a Universal Windows Platform app for planning and tracking your work schedule. For more on the development of this app and the ideas behind it, see my Time Tortoise category page.

Last week, I wrote about techniques to find a list of the .NET assemblies that xUnit.net needs to run unit tests. To review:

• When Visual Studio compiles a unit test project, it doesn’t copy all of the required dependencies into the output directory.
• Therefore, we need a way to manually find these dependencies.
• To find the dependencies, we can attempt to run unit tests and look for detailed error messages. One messages is shown when a FileLoadException occurs. Another one is found in Assembly Binding Log Viewer output: “All probing URLs attempted and failed.” Using these messages, we can manually copy dependencies to the unit test project output directory.
• If these debugging techniques uncover the need for two or more assemblies with different versions, binding redirection can be used to define a single version to use.

Missing dependencies prevent Visual Studio from discovering unit tests (i.e., finding them so they can be listed in Test Explorer). Using these debugging techniques, we should be able to successfully discover all of the unit tests in a project. The next step is to try to run the discovered tests.

Although it can be tedious to track down dependencies for the purpose of resolving test discovery problems, there’s a fairly deterministic process (summarized above) to do so. But that’s not the complete solution to getting unit tests working. Successful unit test discovery seems to depend only on the discovery engine finding the correct assemblies in the output directory. Correct seems to be based on the assembly full name (name, version, culture, and public key).

But actually running unit tests leads to a much stricter process, as the machine uses the manually-copied assemblies to resolve method calls and other dependencies. For example, several Time Tortoise integration tests cause this method call to be executed as they start up:

Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.AddQuery(IServiceCollection serviceCollection)

Although I had a set of dependencies that caused all of the integration tests to be successfully discovered, I observed this error when running some of them:

System.TypeLoadException : Inheritance security rules violated by type: 'Remotion.Linq.Parsing.RelinqExpressionVisitor'. Derived types must either match the security accessibility of the base type or be less accessible.

Since the code in question works fine for running the Time Tortoise app itself, it’s unlikely that this error message is due to a bug in Microsoft.EntityFrameworkCore or Remotion.Linq. Instead a combination of two or more assembly versions must be incompatible with each other.

System.BadImageFormatException : An attempt was made to load a program with an incorrect format.