Deploying Additional Files for MSTest Command Line Runner
Tests with Native Dependencies
Whenever I'm developing a non-desktop Windows 8 application (for phones or tablets) I prefer having as much business logic in portable class libraries as possible. Not only does this allow me to reuse it for different platforms, it also makes running the tests simpler, because they don't need to be run in an isolated WinRT or Windows Phone container. The test project can be a standard .NET class library, allowing mocking frameworks and other helper libraries to be used which are not available elsewhere.
As long as the test project for the portable class library doesn't require any native platform specific dependencies, this works great. However, having a dependency on SQLite can still complicate things a bit, if any of the tests require the native
sqlite3.dll library to be present. Usually this shouldn't be the case, but if you're writing a SQLite wrapper for your application, you will probably want to test it just like all the other code and therefore need the SQLite engine.
For this to work in a desktop application (or a desktop test project), you will need to include the native
sqlite3.dll library into your project and in its properties set the Copy to Output Directory value to Copy always. This way the library will be copied to the build output directory along with .NET assemblies, making sure it will be found by any code requiring it. Since only a 32-bit precompiled version of the library is available, the test project needs to be compiled for
x86 target platform, but this shouldn't be a problem.
Running the Tests from Command Line
To run the tests inside Visual Studio (either with the built-in test runner or a 3rd party one) that's already enough. Nevertheless, the same tests run from the command line using
MSTest.exe will fail because of the missing
System.DllNotFoundException: Unable to load DLL 'sqlite3': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
This happens because by default MSTest is running the tests in isolation, which involves copying the required files to a different directory. Of course
MSTest.exe is not aware that our tests require
sqlite3.dll, therefore it doesn't copy the file along with the assemblies.
The easy solution would be to disable the isolation. This way, the assemblies will not be copied anywhere, hence they will be run from the directory which also contains the required
MSTest.exe /testcontainer:.\Portable.Tests\bin\Debug\Portable.Tests.dll /noisolation
A more proper solution would be to tell MSTest, that the missing library needs to be deployed together with the assemblies. You can do that by creating a file with
.testsettings extension in the solution folder, with the following contents:
<?xml version="1.0" encoding="UTF-8"?> <TestSettings name="TestSettings" id="350a1732-b798-4794-b711-15b44e8504e9" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"> <Deployment> <DeploymentItem filename="Portable.Tests\sqlite3.dll" /> </Deployment> </TestSettings>
You will want to create the file from within Visual Studio and have it added as a solution item. Double-clicking it inside Visual Studio will open a wizard for editing it, so that you don't need to learn the XML structure. Also, in this case the built-in Visual Studio runner will take into account these same settings, when running the tests.
To successfully run the tests from command line, you will need to pass the
.testsettings filename to MSTest. The tests will now pass:
MSTest.exe /testcontainer:.\Portable.Tests\bin\Debug\Portable.Tests.dll /testsettings:.\TestSettings.testsettings
Running the Tests on the Build Server
Of course, the
.testsettings file must be committed to source control, to make it available to other developers and on the build server. When configuring the test runner on the build server, the
.testsettings file must be specified. The exact details depend on the build server used.
TeamCity has a dedicated runner for MSTest, accepting the path to the
.testsettings file as one of the parameters (just make sure you Show advanced options to see it):