V3 Documentation
Search

Assembly and Resource Co-location Assumptions

Description

A major feature of NCrunch is its ability to selectively build only projects that have changed, as opposed to building a changed project and all projects that depend on it (which is the Visual Studio norm). For this feature to work correctly, NCrunch avoids copying a project's referenced assemblies into its build output directory after the project is built. Under normal unit testing conditions, this is safe to do as NCrunch is able to artificially wire together testing environments using its knowledge of which referenced assemblies should exist in the environment.

However, this can cause problems with some build steps or tests that have been written in the assumption that referenced assemblies will always be co-located with assemblies that reference them.

These problems also extend to resource files that have been marked to be copied to project build output directories. Under a normal VS build, these resource files will be co-located with the output assemblies of referencing projects. Under NCrunch, this is not always the case.

Possible Problem Example

Consider the following test code:

[Test]
public void TestReferencingCoLocatedAssembly()
{
    // When running with a standard test runner, ReferencedAssembly.dll is
    // always in the same directory as this assembly (the current directory)
    var expectedReferencedAssemblyFilePath = "ReferencedAssembly.dll";

    // When running in NCrunch, this line will explode with an assertion failure
    Assert.That(File.Exists(expectedReferencedAssemblyFilePath));
}

The above test will fail when executed with the default settings in NCrunch, but it will pass in any standard test runner. The test expects that ReferencedAssembly.dll is always in the current directory adjacent to the test assembly referencing it, but in a normal NCrunch build situation this is not the case.

Some frameworks (such as IoC containers, MS Code Contracts and MS Test private method accessors) are designed to assume that output assemblies will always be co-located. This can create obscure assembly resolution problems while tests are being executed by NCrunch.

Solutions

Copy Referenced Assemblies To Workspace

One way to work around these problems using NCrunch is to turn on the Copy referenced assemblies to workspace project-level configuration setting for projects that are involved. This will deactivate NCrunch's build optimisations, copying the ReferencedAssembly.dll file into the output directory of the test project as a build action normally would. This approach is generally only used as a last resort, as NCrunch's build optimisations greatly reduce test cycle times. This approach will also enable the copying of resource files to referencing project output directories.

In situations where NCrunch detects a well known framework that requires assemblies to be co-located, it will often enable the Copy referenced assemblies to workspace setting automatically.

Use Pre-loaded Assembly Locations

Another approach is to instead rely on the wiring already existing in NCrunch's test environment to find the referenced assembly:

[Test]
public void TestReferencingCoLocatedAssembly()
{
    // NCrunch has already loaded ReferencedAssembly into the current 
    // application domain, so we can discover the true location of 
    // this assembly using any type within it
    var expectedReferencedAssemblyFilePath = 
        typeof(ReferencedAssembly.ReferencedClass).Assembly.Location;

    // This assertion will pass in NCrunch and any other test runner
    Assert.That(File.Exists(expectedReferencedAssemblyFilePath));
}

Use NCrunch's Knowledge of Referenced Assemblies

The NCrunchEnvironment.GetAllAssemblyLocations method allows you to retrieve the locations of all relevant assemblies that the test environment could potentially make use of. While this feature does exist only for NCrunch (and not for other test runners), it is a very useful way to work around assembly co-location assumptions.

You can find an example of how to use this option in the troubleshooting guide for tests that build their own application domains.