Tuesday, October 20, 2009

Running MSpec with TFS Team Build

Recently, I’ve become a big fan the .net specification framework Machine.Specifications, or MSpec.  It has helped us define our requirements and communicate in a common language with our Business Analysts.  Rob Conery had an awesome introduction to using MSpec. I’m going to piggy-back on his example here.

We use Microsoft Team Foundation Server for our continuous integration (as well as work item tracking…more on that later), and I wanted to be able to provide the MSpec report to our developers and BAs automatically for each build.  Here is what I came up with.

Versions :

  • Microsoft Team Foundation Server 2008 SP1
  • Machine.Specifications 0.2.0.0

This assumes that you already have a Specs project set up.  You will also need Machine.Specifications.ConsoleRunner.exe accessible on the Build Server.  You could either install the ConsoleRunner in a well known folder on the (each) build server, or you could include it right in source control.  I chose to include it in source control.

  • In my Specs project, I created a Dependencies folder and dropped in…
    • CommandLine.dll
    • Machine.Specifications.ConsoleRunner.exe
    • Machine.Specifications.dll
    • Machine.Specifications.NUnit.dll
    • Machine.Specifications.Reporting.dll
  • From the Specs project, I added references to…
    • Machine.Specifications.dll
    • Machine.Specifications.NUnit.dll. 
  • For each of the dependencies above, I made sure to set the “Build Action” to “Content” and the the “Copy to Output Directory” to “Copy Always”.  Again, you could just include these files in a folder such as “C:\Program Files\MSpec” and call it there. 

So, my solution ended up looking like this.

MSpecSolution

  • In Team Explorer, I had previously created a continuous integration build, named “CI Build”.

MSpecCIBuild

  • I set the local folder for the workspace to a known location, at “F:\MSpecTestSource”.

MSpecWorkspace

  • Next, I needed to edit my Team Build project file.  This is located in the TeamBuildTypes folder in the Team Explorer Source Control Window.  You have to Check Out this file, edit it, and Check it back In.

MSpecTeamBuildTypes

MSpecCheckOutBuild

  • Finally, in the TFSBuild.proj file, I added a target to run the MSpec Console Runner.  By default the TFSBuild.proj file ends with

<ItemGroup>
<!--  ADDITIONAL REFERENCE PATH
The list of additional reference paths to use while resolving references. For example:

<AdditionalReferencePath Include="C:\MyFolder\" />
<AdditionalReferencePath Include="C:\MyFolder2\" />
-->
</ItemGroup>
</Project>


  • Copy the following code after the </ItemGroup> and before the </Project> tags


<Target Name="AfterDropBuild">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Name="Mspec Report"
Message="Running MSpec">
</BuildStep>

<PropertyGroup>

<!--The Path to the console runnner-->
<MSpecConsoleRunner>&quot;$(BuildDirectory)\Binaries\Release\Dependencies\Machine.Specifications.ConsoleRunner.exe&quot;</MSpecConsoleRunner>

<!--The Path to the Spec file-->
<MSpecSpecClass>&quot;$(BuildDirectory)\Binaries\Release\MSpecTest.Specs.dll&quot;</MSpecSpecClass>

<!--Html or Xml?-->
<MSpecReportFormat>--html</MSpecReportFormat>

<!--The drop location for the report-->
<MSpecReportPath>&quot;$(DropLocation)\$(BuildNumber)\MSpecTestReport.html&quot;</MSpecReportPath>

<MSpecCommandLine>$(MSpecConsoleRunner) $(MSpecSpecClass) $(MSpecReportFormat) $(MSpecReportPath)</MSpecCommandLine>
</PropertyGroup>

<Exec Command="$(MSpecCommandLine)" ContinueOnError="true" />
</Target>


The variable names (<MSpecConsoleRunner />, <MSpecSpecClass />) are insignificant.  You will need to change the variables to point to your paths.  I created some MSBuild debug statements that you can copy and paste below the </BuildStep> and above the <PropertyGroup> above to help determine what paths Team Build is using.  You can view these messages in the BuildLog for each build.



<!--<Message Text="DEBUGMESSAGE:BinariesRoot = $(BinariesRoot)" />
<Message Text="DEBUGMESSAGE:BinariesSubdirectory = $(BinariesSubdirectory)" />
<Message Text="DEBUGMESSAGE:BuildAgentName = $(BuildAgentName)" />
<Message Text="DEBUGMESSAGE:BuildAgentUri = $(BuildAgentUri)" />
<Message Text="DEBUGMESSAGE:BuildBreak = $(BuildBreak)" />
<Message Text="DEBUGMESSAGE:BuildDefinition = $(BuildDefinition)" />
<Message Text="DEBUGMESSAGE:BuildDefinitionId = $(BuildDefinitionId)" />
<Message Text="DEBUGMESSAGE:BuildDefinitionName = $(BuildDefinitionName)" />
<Message Text="DEBUGMESSAGE:BuildDefinitionUri = $(BuildDefinitionUri)" />
<Message Text="DEBUGMESSAGE:BuildDirectory = $(BuildDirectory)" />
<Message Text="DEBUGMESSAGE:BuildNumber = $(BuildNumber)" />
<Message Text="DEBUGMESSAGE:BuildProjectFolderPath = $(BuildProjectFolderPath)" />
<Message Text="DEBUGMESSAGE:BuildUri = $(BuildUri)" />
<Message Text="DEBUGMESSAGE:ConfigurationFolderUri = $(ConfigurationFolderUri)" />
<Message Text="DEBUGMESSAGE:DropLocation = $(DropLocation)" />
<Message Text="DEBUGMESSAGE:IsDesktopBuild = $(IsDesktopBuild)" />
<Message Text="DEBUGMESSAGE:LastBuildNumber = $(LastBuildNumber)" />
<Message Text="DEBUGMESSAGE:LastGoodBuildLabel = $(LastGoodBuildLabel)" />
<Message Text="DEBUGMESSAGE:LastGoodBuildNumber = $(LastGoodBuildNumber)" />
<Message Text="DEBUGMESSAGE:MachineName = $(MachineName)" />
<Message Text="DEBUGMESSAGE:MaxProcesses = $(MaxProcesses)" />
<Message Text="DEBUGMESSAGE:Port = $(Port)" />
<Message Text="DEBUGMESSAGE:NoCICheckinComment = $(NoCICheckinComment)" />
<Message Text="DEBUGMESSAGE:Reason = $(Reason)" />
<Message Text="DEBUGMESSAGE:RequestedBy = $(RequestedBy)" />
<Message Text="DEBUGMESSAGE:RequestedFor = $(RequestedFor)" />
<Message Text="DEBUGMESSAGE:SolutionRoot = $(SolutionRoot)" />
<Message Text="DEBUGMESSAGE:SourceGetVersion = $(SourceGetVersion)" />
<Message Text="DEBUGMESSAGE:SourcesSubdirectory = $(SourcesSubdirectory)" />
<Message Text="DEBUGMESSAGE:StartTime = $(StartTime)" />
<Message Text="DEBUGMESSAGE:TeamBuildConstants = $(TeamBuildConstants)" />
<Message Text="DEBUGMESSAGE:TeamBuildOutDir = $(TeamBuildOutDir)" />
<Message Text="DEBUGMESSAGE:TeamBuildRefPath = $(TeamBuildRefPath)" />
<Message Text="DEBUGMESSAGE:TeamBuildVersion = $(TeamBuildVersion)" />
<Message Text="DEBUGMESSAGE:TeamFoundationServerUrl = $(TeamFoundationServerUrl)" />
<Message Text="DEBUGMESSAGE:TeamProject = $(TeamProject)" />
<Message Text="DEBUGMESSAGE:TestResultsRoot = $(TestResultsRoot)" />
<Message Text="DEBUGMESSAGE:WorkspaceName = $(WorkspaceName)" />-->

There should now be a Report.html in the drop location. To view this, right click on the build in the Build Explorer, and choose "Open Drop Folder"

1 comment:

  1. I just saw Rob Conery's great intro to MSpec too - excited to try this out in TFS 2010 Beta 2. Thanks for the walk-through!

    ReplyDelete