(Lots of intro, as I suspect I may have an X-Y problem...)
======================
Massive Context and Preamble
======================
I have a solution which is doing some stuff about opening and using DLLs. Naturally some of that stuff is dependant upon what classes and interfaces are defined within that dll, and I'd like to test that fuctionality.
I could just create some dlls once and store those files as test resources, but I thought it was nicer to add to the sln some .csprojs that would build dlls with the relevant properties for the tests. Thus I have a structure that looks something like this:
The code in DLLReader is expecting to be given a Single Directory which contains all the dlls it's interested in; this is `FileStore`. Each of the TestCase projects builds its dll, and then xcopies it up to FileStore as a post-build step. The test code tells DLLReader about SlnRoot/FileStore and everything is happy.
This all works really nicely when run by VS native Testing, or by Resharper.
But when I run it in NCrunch, it all falls apart.
If I've previously built the project in VS, then when running in NCrunch I can locate the `.GetOriginalSolutionPath()` and find the FileStore which will have the dlls in, and that all works.
But if NCrunch is the thing doing the build then the TestCase projects dont copy their dlls to the FileStore. I would like NCrunch to be able to do ALL the work itself, if necessary - it seems bad to write a project where the test will work (soto voce... but only if you've explicitly done a build in VS beforehand )
At the first level this is because the post-build doesn't run at all :) but if I actively turn post-build ON in NCrunch, then the post build is running in NCrunch's copy of the whole solution file system, which is subtly differently laid out. Notably, there's an extra layer of folders before you get to a common root, and FileStore itself hasn't been copied over at all.
===========
Actual Question
===========
I think what I want is to be able to tell NCrunch to please do actually build the original solution and run its post-build steps (in situ, on the original solution). If necessary it can do that as well as runing its own build, but I do need it to run the original post-builds, in the original folders.
A) Can I do that?
B) If I can't what is the idiomatic NCrunch way to solve this problem, or to restructure things so that I don't have this problem.
By having the post build steps copying these DLLs into your resource location, you've effectively introduced an implicit dependency from DLLReader.Tests to your TestCaseExamples. This is not a dealbreaker and it can be handled, it just needs a bit more plumbing.
You'll want to try and keep this entirely within the NCrunch workspaces and avoid touching the foreground solution. In this way, you won't introduce a dependency on your foreground build.
The cleanest way to solve this is to revise the logic in DLLReader.Tests that is used for finding these resource DLLs. Right now you're probably just iterating over a known directory in the file system. Instead of doing this, you'll need to use the methods on NCrunch.Framework.NCrunchEnvironment (GetAllAssemblyLocations and/or GetImplicitlyReferencedAssemblyLocations) to find the output locations of your TestCaseExamples in the workspaces NCrunch has built for them. For this to work, you'll need to have either project references from the DLLReader.Tests project pointing at the TestCaseExample projects, or you'll need to use the implicit project dependencies setting to hook them up.
By having the post build steps copying these DLLs into your resource location, you've effectively introduced an implicit dependency from DLLReader.Tests to your TestCaseExamples
Yes, indeed. Further this is a logical dependency, not merely an implementation detail ... Those tests are literally intended to depend on those TestCase Projects :)
I have already added build dependencies in the solution between the relevant projects, so that building the Test project forces a build of the TestCase Projects (i.e. lines in the .sln file)
Is this what you meant by
you'll need to have either project references from the DLLReader.Tests project pointing at the TestCaseExample projects
Or doing you mean that the references need to be defined on the csproj - as if it were referencing a library or directly calling another projects public classes?
Thanks for pointing me towards GetAllAssemblyLocations and/or GetImplicitlyReferencedAssemblyLocations; I'll give them a go.
Assuming I can get to a point where my test helper can see the files ... Are you recommending that I do the copying into a single folder within the test setup? Or that I re-write the code interface to so taht the test file fetcher gets each relevant file from its own sub-directory at the relevant point?
Brondahl wrote:
Or doing you mean that the references need to be defined on the csproj - as if it were referencing a library or directly calling another projects public classes?
If there's already a ProjectReference (MSBuild XML tag) pointing at the dependencies from the Tests project, then you should already be very close. When this is declared, GetAllAssemblyLocations will let you find the DLLs for these projects as built by NCrunch.
Brondahl wrote:
Assuming I can get to a point where my test helper can see the files ... Are you recommending that I do the copying into a single folder within the test setup? Or that I re-write the code interface to so taht the test file fetcher gets each relevant file from its own sub-directory at the relevant point?
Either option should work, but adjusting the code to load directly from the DLLs without copying them would be much cleaner. You'll probably need to make the code conditional or use a fallback so that it can still work without NCrunch (assuming you want it to be able to work with other runners also).
I started with code to copy into the core folder, as the first step to get the tests passing, then did some heavy duty refactoring to end up with a PathProvider dependency, which gets a injected differently if NCrunch is active and uses that GetAllAssemblyReferences method.