I have an MVC 3 demo site which I wrote to accompany a series of blog posts located here: bitbucket
The project uses an extension method from ApprovalTests and the extension method works fine under ordinary debugging, under the Visual Studio test runner and under the DevExpress test runner.
But when the test runs under NCrunch, it fails with a MissingMethodException:
System.MissingMethodException: Method not found: 'System.Web.Mvc.ViewResult ApprovalUtilities.Asp.Mvc.MvcUtilites.Explicit(System.Web.Mvc.ViewResult)'.
at MVCTestSite.Controllers.HomeController.Index(IEnumerable`1 cars)
at MVCTestSite.Controllers.HomeController.Index() in ...\HomeController.cs:line 26#0
at MVCTestSite.Tests.Controllers.HomeControllerTest.Index() in...\HomeControllerTest.cs:line 24#1
I can work around the problem with the NCRUNCH constant:
Reversing the condition produces the missing method exception. I've tried a variety of configuration options and the exception is petty consistent. Just a few things I can remember trying:
[list=1]
Instrumentation on/off
Include ..\**.*
Use ordinary static method syntax instead of extension syntax.
Debug vs Release
Any CPU vs x86
MSTest vs xUnit
v1.38b vs v1.39b
Maybe I'm missing something obvious, or maybe not. Hopefully the example code can help track down the problem.
Thanks for posting! I've just tried running your source code myself and it seemed to work without problems for me, so I suspect this is caused by something in your environment.
Does the problem occur consistently for you? Or does a reset of the NCrunch engine rectify it?
Make sure to check the contents of the GAC to be sure that the version of approval tests you are referencing with your project is not being confused with another version of the same assembly already installed on your machine. At runtime, the CLR will always prefer to reference GAC assemblies where strong names are available - even though NCrunch may have built your project against a different assembly.
The problem is consistent, resetting the engine doesn't fix it. I also checked the GAC as you suggested but there aren't any copes of ApprovalUtilites in there.
I've got two different machines that the test is failing on. I set up both machines so it's possible I introduced the same variable into both environments. If you have any more ideas let me know, in the meantime I'll see if I can reproduce the error in a clean VM or something.
I think what would be interesting is to try and find the assembly being resolved by NCrunch's test environment and find out why it's different to the one you've referenced.
Try breaking into the code using NCrunch's debugging options and have a look at the list of modules loaded using your debugger. NCrunch will always pre-load the assemblies it believes are required in the test domain, so the DLL should already exist in the list. Which DLL was loaded? Is it the one from the NCrunch workspace? Does it have any structural differences to the ApprovalTests DLL referenced from your project in the solution?
I followed your suggestion and I think I've isolated the problem. After breaking into the code and looking at the loaded modules, everything seemed fine. The correct version of ApprovalUtilities was loaded. Poking around in the debugger, I tried executing the bad line of code in the quick watch window, and later in the immediate window. In the immediate window I got this:
ApprovalUtilities.Asp.Mvc.MvcUtilites.Explicit(View(repository.GetCars())) The best overloaded method match for 'ApprovalUtilities.Asp.Mvc.MvcUtilites.Explicit(System.Web.Mvc.ViewResult)' has some invalid arguments
ApprovalUtilities.Asp.Mvc.MvcUtilites.Explicit(new ViewResult()) The type or namespace name 'ViewResult' is not valid in this scope
Since two versions of MVC are loaded, the runtime appears to be having trouble deciding what a ViewResult is, and can't decide whether it can use the extension method in ApprovalUtilities. I debugged the same test under the VS test runner and sure enough, only MVC v3 was loaded. I uninstalled MVC v2 from one of my machines, and sure enough, the test works in NCrunch now. You could probably reproduce on your side by grabbing MVC v2.
The project is MVC 3, but ApprovalUtilities is built against MVC 2, so this is probably why NCrunch decided to load it. I still have v2 on my machine because I still have some sites that are built against v2. If you think I'm right that the two versions of MVC are causing the problem, then I'm happy at this point to know what the problem was. But I'd also like to put MVC 2 back on the machine at some point in the future.
You've hit the nail on the head with the duplicate assemblies. The CLR tends to get confused if two assemblies with the same name are loaded into the App Domain, and in this situation it can be tough to know what is the correct answer - as the runtime code seems to depend upon both versions of the file at the same time.
NCrunch is supposed to statically bind its test runtime to the same DLLs that were used to build a project, so an interesting piece of information for me would be to establish which version of System.Web.Mvc was used during the build of your project. I have some changes in progress that may solve this problem in a future version, but until we can find out which DLL was used in the build, I can't be sure they'll work.
I can have a go at reproducing this situation locally. I'm wondering if there's any chance you might be interested in loading MVC2 back into your GAC and having a look for me which assembly was resolved in the build, as this would reduce the risk that I'm unable to accurately reproduce your build environment. The easiest way to find out which assemblies NCrunch is building against is through the following steps:
1. Open the solution, let it load the projects and build them all
2. Right click on the built project in the Tests Window (you may need to adjust the filter to do this)
3. Choose Advanced->Browse to workspace
4. Find the .proj file in the workspace that NCrunch has constructed for the build
5. Open the .proj file and examine the <AssemblyReference> tags to see which assemblies are being referenced.
I've had another go at reproducing this with both versions of MVC installed, though it all seems to run properly for me.
I still think there must be something else in your environment that is causing a difference in behaviour. I'd really like to have a look at a bug report from your NCrunch after you've loaded the project and finished a full build/test run. I realise I'm asking you to install MVC2 all over again, so please only do this if you can make time for it.