Recently, while i was setting up TFS build [to enable CI] for a project at Telerik that i am currently working on, i came across configuring it up with XUnit for doing automated tests every time someone checks-in source code after the build process is complete using the Xunit MsBuild task and additionally it will print the build steps so that developer knows what’s the issue.
Now, to layout a little bit of background. When you setup a “New build Definition” from Team Explorer – > Builds . TFS actually drops a TFSBuild.proj under “$/ProjectName/TeamBuidTypes/BuildDefinationName/” folder . This is nothing but a regular MSBuild file that defines how and where your project should be built and dropped and how it will be tested.
TFS gives out of the box support for VSTests , but for other types you need to do a little bit of configuration.When you do a check-in[If build process is setup to fire for each check-in],first TFS does a GET operation from /TeamBuildTypes/BuildDefinationName/ and after that it starts executing as things are defined in TFSBuild.proj. In the pasted Team explorer snap you can see that i have added a target file named “XunitBuild.targets”. It is referenced in TFSBuild.proj somewhere above the <itemGroup> like
1: <Import Project="$(MSBuildProjectDirectory)\XUnitBuild.targets" />
To use xunit task we need to include the following two assemblies in same folder with TFSBuild.proj.
Inside XUnitBuild.targets, first of all we need to reference the task. Just to mention that assembly file path will be preceded by a folder path where TFSBuild.proj is stored though GET operation.
1: <UsingTask AssemblyFile="xunit.runner.msbuild.dll"
Next, we need to hook the task under specific target that will be invoked by TFS automatically. TFS has some predefined targets that are fired at different steps of the build process, we can hook them to perform some custom tasks before, during or after each step is completed.
Some of the targets are :
You can get the full list from MSDN but i would rather leave it up to you as it is not the main topic here. Moving forward, i have hooked CoreTest Target [ set Name=”CoreTest”]. Inside it, i created a build step that will print if tests are successful or failed depending on the result returned by xunit task.
4: Message="Preparing to test...">
5: <Output TaskParameter="Id" PropertyName="StepId" />
On first run BuildStep task will produce an Id that will be stored in a property which will be used later to print test status.
1: <xunit ContinueOnError="true" Assembly="$(OutDir)\Sample.Tests.dll" Verbose="true">
2: <Output TaskParameter="ExitCode" PropertyName="Result" />
Executing xunit task requires the assembly path for tests to perform on, additionally you can specify it to be verbose and finally can store the return code in a property for further checks. Here, i have also specified “ContinueOnError=true” as for any failure, things will be handled manually.
1: <BuildStep Condition="'$(Result)'=='0'" TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
2: BuildUri="$(BuildUri)" Id="$(StepId)" Status="Succeeded" Message="Test succeeded." />
3: <BuildStep Condition="'$(Result)'!='0'" TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
4: BuildUri="$(BuildUri)" Id="$(StepId)" Status="Failed" Message="Test Failed." />
6: <Error Condition="'$(Result)'!='0'" Text="Test failed."/>
Above two BuildStep tasks will print test status depending on the result set by xunit task for previously created step and followed by Error task will fail the build for same. Finally, when we will be expanding the build explorer, list will contain the custom step where build status will be determined from the error condition.
That’s it and here goes the target file XUnitBuild.zip . I actually binged it out and covered all together in one place so that it saves a little time of yours so feel free to suggest anything that comes to your mind.