Thanks to a comment from Daniel Richardson on my previous MSTest post (and a lot more research, testing, & debugging), I’ve found a more flexible way of calling MSTest from MSBuild. The main drawback of the solution I blogged about earlier was that new test assemblies added to the solution would not be run in MSBuild unless the Exec call to MSTest.exe was updated to include them. But thanks to a combination of MSBuild transforms and batching, this is no longer necessary.
First, I needed to create a list of test assemblies. The solution is structured in a way that makes this relatively simple. All of our test assemblies live in a “Tests” folder, so there’s a root to start from. The assemblies all have the suffix “.Test.dll” too. The following CreateItem task does the rest:
<CreateItem Include=”$(TestDir)**bin$(Configuration)*.Test.dll” AdditionalMetadata=”TestContainerPrefix=/testcontainer:”>
<Output TaskParameter=”Include” ItemName=”TestAssemblies” />
</CreateItem>
The task above creates a TestAssemblies element, which contains a semicolon-delimited list of paths to every test assembly for the application. Since the MSTest command line needs a space between each test assembly passed to it, the TestAssemblies element can’t be used as-is. Each assembly also requires a “/testcontainer:” prefix. Both of these issues are addressed by the combined use of transforms, batching, and well-known metadata as shown below:
<Exec Command=””$(VS90COMNTOOLS)..IDEmstest.exe” @(TestAssemblies->’%(TestContainerPrefix)%(FullPath)’,’ ‘) /runconfig:localtestrun.testrunconfig” />
Note the use of %(TestContainerPrefix) above. I defined that metadata element in the CreateItem task. Because it’s part of each item in TestAssemblies, I can refer to it in the transform. The %(FullPath) is well-known item metadata. For each assembly in TestAssemblies, it returns the full path to the file. As for the semi-colon delimiter that appears by default, the last parameter of the transform (the single-quoted space) replaces it.
The end result is a MSTest call that works no matter how many test assemblies are added, with no further editing of the build script.
Here’s a list of the links that I looked at that helped me find this solution: