I do some serialization to and from disk and I've got a number of tests that run just fine in Visual Studio's testing environment. NCrunch marks these tests as failing because it is unable to cast things properly.
Here's the full exception:
System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidCastException: [A]XMLUserSchema cannot be cast to XMLUserSchema. Type A originates from 'PortalBackend, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at location 'C:\Users\cthomas\AppData\Local\NCrunch\12960\6\bin\Debug\PortalBackend.dll'. Type B originates from 'PortalBackend, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Users\cthomas\AppData\Local\NCrunch\12960\6\TestResults\f4061339-622e-4352-864e-f6e13ae7b1c5\Out\PortalBackend.dll'.
When I run these tests within NCrunch one at a time they pass. Otherwise, they fail. Yes, I tried putting NCrunch into single thread mode -- they still fail.
I presently have NCrunch configured to copy referenced assemblies into the workspace as my projects don't build in it without that flag. Any help would be appreciated.
Remco NCrunch Developer
#2142
11 Jun 2012 21:34 UTC
Hi, thanks for posting!
It looks to me like there may be an issue with the same assembly being loaded into the application domain twice. You may be able to confirm or deny this by stepping with the debugger to the exception point, then checking the list of loaded modules for duplicates.
Are you doing any manual loading of assemblies into your application domain through the use of Assembly.LoadFrom? Or are you using any frameworks (such as an IOC container) that may do this?
It also looks as though you have a DLL file somehow being copied to your MSTest 'Out' directory. NCrunch normally won't do this, so I suspect something else is also going on here. Basically, we need to ensure that the PortalBackend.dll file in the MSTest 'Out' directory does not get loaded into the test application domain.
It looks to me like there may be an issue with the same assembly being loaded into the application domain twice. You may be able to confirm or deny this by stepping with the debugger to the exception point, then checking the list of loaded modules for duplicates.
Are you doing any manual loading of assemblies into your application domain through the use of Assembly.LoadFrom? Or are you using any frameworks (such as an IOC container) that may do this?
It also looks as though you have a DLL file somehow being copied to your MSTest 'Out' directory. NCrunch normally won't do this, so I suspect something else is also going on here. Basically, we need to ensure that the PortalBackend.dll file in the MSTest 'Out' directory does not get loaded into the test application domain.
Remco wrote:
It looks to me like there may be an issue with the same assembly being loaded into the application domain twice. You may be able to confirm or deny this by stepping with the debugger to the exception point, then checking the list of loaded modules for duplicates.
Breaking in the execution of the test doesn't show any duplicates but I'm hesitant to say that proves anything as the only time I can get the errors to occur is NCrunch's automated test process. Manually triggered tests work fine and those are the only ones I can do in debug mode.
Remco wrote:Are you doing any manual loading of assemblies into your application domain through the use of Assembly.LoadFrom? Or are you using any frameworks (such as an IOC container) that may do this?
Not on purpose! I've got a fairly standard back-end library and some web-services which I've added to a VS Solution along with the unit tests. I've thusfar allowed VS to manage all of my assembles and whatnot so if I'm doing anything of the sort it's certainly not intentional.
Remco wrote:It also looks as though you have a DLL file somehow being copied to your MSTest 'Out' directory. NCrunch normally won't do this, so I suspect something else is also going on here. Basically, we need to ensure that the PortalBackend.dll file in the MSTest 'Out' directory does not get loaded into the test application domain.
Will NCrunch do that if it's configured to copy referenced assemblies into the workspace? NCrunch won't build the solution without that flag so I had to set it. Otherwise I've left the default configuration alone.
I do have a Post-build event on the project that generates PortalBackend.dll, but NCrunch says it's configured not to run those.
Thanks in advance for your help!
Remco NCrunch Developer
#2152
12 Jun 2012 21:32 UTC
When you break in with the debugger, are you doing this using NCrunch's debug features, or with another test runner? Note that other test runners won't use the same assembly resolution logic as NCrunch, so you probably won't see any useful information from them around this issue. Does the NCrunch debug option allow you to step into your test?
With the post build event that generates portalbackend.dll, is this done using a custom MSBuild step (i.e. <Target Name="AfterBuild">) or with the <PostBuildEvent> MSBuild property? Note that NCrunch will only disable the later by default (not the former).
Something that may be interesting to try is to put a line at the top of the failing test that specifically deletes the PortalBackend.dll from the 'Out' directory. For example:
if (File.Exists("PortalBackend.dll")) File.Delete("PortalBackend.dll");
While I wouldn't recommend this as a permanent solution, it would help us to establish if the problem is in fact being caused by duplicate assemblies.
With the post build event that generates portalbackend.dll, is this done using a custom MSBuild step (i.e. <Target Name="AfterBuild">) or with the <PostBuildEvent> MSBuild property? Note that NCrunch will only disable the later by default (not the former).
Something that may be interesting to try is to put a line at the top of the failing test that specifically deletes the PortalBackend.dll from the 'Out' directory. For example:
if (File.Exists("PortalBackend.dll")) File.Delete("PortalBackend.dll");
While I wouldn't recommend this as a permanent solution, it would help us to establish if the problem is in fact being caused by duplicate assemblies.
Remco wrote:When you break in with the debugger, are you doing this using NCrunch's debug features, or with another test runner? Note that other test runners won't use the same assembly resolution logic as NCrunch, so you probably won't see any useful information from them around this issue. Does the NCrunch debug option allow you to step into your test?
NCrunch, but again, it only seems to error if I'm allowing NCrunch to start the run with a build of some kind. Simply running the tests absent a build (which is the only way I can find to run NCrunch in debug mode) doesn't throw an error.
With the post build event that generates portalbackend.dll, is this done using a custom MSBuild step (i.e. <Target Name="AfterBuild">) or with the <PostBuildEvent> MSBuild property? Note that NCrunch will only disable the later by default (not the former).
I think it's using the former but I'll check when I'm back at work tomorrow and let you know.
Something that may be interesting to try is to put a line at the top of the failing test that specifically deletes the PortalBackend.dll from the 'Out' directory. For example:
if (File.Exists("PortalBackend.dll")) File.Delete("PortalBackend.dll");
While I wouldn't recommend this as a permanent solution, it would help us to establish if the problem is in fact being caused by duplicate assemblies.
I'll get right on that; just knowing something would be a huge benefit at this point.
Remco wrote:It also looks as though you have a DLL file somehow being copied to your MSTest 'Out' directory. NCrunch normally won't do this, so I suspect something else is also going on here. Basically, we need to ensure that the PortalBackend.dll file in the MSTest 'Out' directory does not get loaded into the test application domain.
So I had this flash of insight while trying to work out how to meaningfully accomplish that. The only file in the "Out" directory was the PortalBackend.dll file and that just seemed strange. I noticed that some of the auto-generated tests I'd started with contained a [DeploymentItem("PortalBackend.dll")] tag in front of them and it occured to me that I didn't actually know what that was for (since deployment isn't something I want my tests managing right now).
I removed that tag from all tests and it looks as if the issues has solved itself now.
So, lesson learned: DeploymentItem tags can break serialization and deserialization if the class you're serializing to/from is within the dll you're deploying.
I still have no idea why NCrunch breaks on that and the normal test process doesn't. Is NCrunch looking for .dlls in the Out directory? Is that a good idea?
Remco NCrunch Developer
#2169
13 Jun 2012 22:32 UTC
Good find. The DeploymentItem would definitely cause the assembly to be copied into the 'Out' dir, and most likely the dynamics of the code under execution (and the CLR doing its thing) would result in the assembly being duplicated inside the test process. Most likely this would affect more than just serialization.
The problem has shown itself in NCrunch because NCrunch has wildly different assembly resolution logic compared with other test runners. The reasoning behind these differences is purely around performance - NCrunch is able to perform partial builds and wire together test domains artificially, while other test runners are more conventional and will typically rely on the original assemblies being co-located. There are changes pending in the upcoming 1.40b release that should simplify this a bit and may actually also resolve the problem you've described.
Most likely you had the DeploymentItem attribute in-place in an effort to solve a dynamic resolution problem of some kind. I'm glad you managed to work it out!
Cheers,
Remco
The problem has shown itself in NCrunch because NCrunch has wildly different assembly resolution logic compared with other test runners. The reasoning behind these differences is purely around performance - NCrunch is able to perform partial builds and wire together test domains artificially, while other test runners are more conventional and will typically rely on the original assemblies being co-located. There are changes pending in the upcoming 1.40b release that should simplify this a bit and may actually also resolve the problem you've described.
Most likely you had the DeploymentItem attribute in-place in an effort to solve a dynamic resolution problem of some kind. I'm glad you managed to work it out!
Cheers,
Remco
Post a reply
Log in to reply.