I had the recent experience of setting up builds with TFS 2008 that build .NET 4 projects. The requirements were that each build would 1) build the solution, 2) run the tests and 3) generate a coverage report. The process was very arduous because the TFS build process isn’t well documented and has limited customization. One thing I had going for me, though, was that the tests are written on the MsTest framework, which plays nice with TFS builds.
These are the steps that I went through:
- Installed TFS 2008 Build on the build server. This included installing a limited version of Visual Studio 2008.
- Installed Visual Studio 2008 on my local computer even though I primarily use 2010. I found out by trial that Visual Studio 2010 does not include many of the dialogs required to manage TFS 2008 builds because it’s designed for TFS 2010.
- Added the build server to the list of build agents in TFS.
- Created a build definition for the project. I did this through Visual Studio 2008 because, again, it has better dialogs for TFS 2008. For example, it created for me the skeleton MsBuild script required for the build.
- For the build configuration, I decided not to use a .vsmdi to define what tests to run. Instead, I chose to run tests within testing assemblies selected by the TestContainer wildcard. The reasoning for this is that whenever someone would add a test to the test harness, he would have to both add the test and include the test in the .vsmdi file. I could foresee that .vsmdi file getting very out of date. With the TestContainer wildcard, the test would be automatically picked up.
- Added a .testsettings file to the build definition. When you use a .vsmdi file, the .testsettings file is already registered. But since I wasn’t using that setup, I had to add the .testsettings file directly to the msbuild script (TFSBuild.proj). I did this by adding a RunConfigFile element with the name of the .testsettings file to the first PropertyGroup element.
- When I started running the builds, I realized that VS2008 defaulted the TFSBuild.proj file to use .NET 3.5. This doesn’t work for a .NET 4 project. I promptly changed the tool version in the root element of the msbuild file from 3.5 to 4.
- Changed one more thing to get the build server to build .NET 4. On the server, I configured the msbuild path to .NET 4 in the MsBuildPath setting in tsbuildservice.exe.config (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies). Then after I restarted the service, it started building with .NET 4.
- Since the solution uses mstest, I then installed Visual Studio 2010 on the build server to get the references to resolve.
- As a result of installing VS2010 on the build server, the TFS build script (C:\Program Files (x86)\Msbuild\Microsoft\Visual Studio\Microsoft.TeamFoundation.Build.targets) was updated for TFS 2010. Of course, this caused the builds to fail. The builds started working again after the file was rolled back. At this point, the solution was building and tests were executing. It was time for requirement 3: test coverage.
- Selected the assemblies to collect coverage statistics on. This is configured in the .testsettings file. VS2010 has a very nice dialog to configure the coverage settings, but it’s a little hidden.
- Select “Data and Diagnostics” on the left.
- Select “Code Coverage” in the list.
- Click on the small Configure button that appears just above the list. This will show the list of assemblies to collect coverage on.
- Finally, the build started generating the coverage as a data.coverage file. But when I opened on the file on the build server, it couldn’t resolve the assembly references. I fixed this by unchecking the “Instrument Assemblies in Place” checkbox in the code coverage configuration dialog for the .testsettings file. This allowed the coverage results to be viewed on the build server.
- The trouble I faced then is that I the coverage results couldn’t be viewed anywhere other than on the build server, which was unacceptable. The mstest coverage generates a binary report file with full path references to the dll’s (specific to the build server), and the file won’t load if the references don’t resolve. I figured out that if I loaded the coverage report into Visual Studio on the build server, I could then export the coverage report as an XML file, but that doesn’t help me in the build process. So, I looked for a command-line utility from Microsoft to do that because most things you can do in Visual Studio, you can do on the command line. No such luck!
I finally found the API calls to export the coverage as XML, and I wrote my own command-line utility to do it. It comes down to three lines of code (shown below). It took me longer to find it than to write it. Next, I executed the utility in the TSFBuild.proj file to generate the XML.
//reference Microsoft.VisualStudio.Coverage.Analysis
// at C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies
var coverageInfo = CoverageInfo.CreateFromFile(“foo.coverage”);
var dataSet = coverageInfo.BuildDataSet(null);
dataSet.ExportXml(“bar.coveragexml”); - The last step was to publish the coverage XML file with the build drop. To do this, I included a copy in the TSFBuild.proj file. Finshed!!!