As MSBuild is getting more and more attention from the community, it was time for MbUnit to propose a MSBuild Task to execute the tests. This is now done (in the CVS) and the task will be available in the next release.
MSBuild Task Creation Process
I have used the MSDN articles on MSBuild as a good source of information for building the MbUnit task (see part 1 and part 2) from Christophe NasarreChristophe Nasarre. More specifically, the part 2 of the articles gives a detailled how-to on the custom task creation. I won't repeat the article here.
First impression: MSBuild way of defining custom properties is rather limited. I would have though that they could have used Xml serialization so that any serializable object could be used in the task definition. That's sad...
Ok, we start by creating a new project, added Microsoft.Build.Framework.dll and Microsoft.Build.Utilities.dll references and we add the MbUnit class.
using System; using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using MbUnit....; // MbUnit usings namespace MbUnit.MSBuild.Tasks { public class MbUnit : Task { public override bool Execute() { ... } } }
As you can see, the MbUnit class derives from Task, which implements ITask. The Execute method is invoked by MSBuild. Next step is to add some parameters to the task in order to setup the test execution. There are a bunch of them, I will focus on the test assembly paths:
public class MbUnit : Task { private string[] assemblies; [Required] public string[] Assemblies { get { return this.assemblies; } set { this.assemblies = value; } }
Note the Required attribute which is used to mark required properties. We can now implement the main loop: the Execute method. The execute method outline is rather simple:
Required
public override bool Execute() { this.result = new ReportResult(); try { foreach (string testFilePath in this.Assemblies) { string path = GetFilePath(testFilePath); if (path==null) return false; using (TestDomain domain = new TestDomain(path)) { domain.ShadowCopyFiles = false; domain.Load(); domain.TestTree.RunPipes(); result.Merge(domain.TestTree.Report.Result); } } this.GenerateReports(); } catch (Exception ex) { this.Log.LogError("Unexpected failure during MbUnit execution"); return false; } return true; }
It is time to prepare the project to debugging the task. As advised in the article, set the output path of the project to the .Net 2.0 folder and use MSBuild.exe as starting program. Put in the command line arguments the name of the XML file containing the MbUnit project.
Sample Project
This is a sample MSBuild project that executes MbUnit tests:
<?
The output of this task is as follows:
Microsoft (R) Build Engine Version 2.0.40607.16 [Microsoft .NET Framework, Version 2.0.40607.16] Copyright (C) Microsoft Corporation 2004. All rights reserved. Target "Tests" in project "ProjectSample.xml" Task "MbUnit" Loading C:\Documents and Settings\dehalleux\My Documents\Projects\mbunit\src\MbUnit.Demo\bin\Debug\MbUnit.Demo.exe Found 3 tests Running fixtures. Tests finished: 3 tests, 1 success, 2 failures, 0 ignored Unloading AppDomain All Tests finished: 3 tests, 1 success, 2 failures, 0 ignored in 1,56469663043767 seconds Generated Xml report at MbUnit.15_26_26.15_26_26.xml Generated Text report at MbUnit.15_26_26.15_26_26.txt Generated Html report at MbUnit.15_26_26.15_26_26.html Generated Dox report at MbUnit.15_26_26.15_26_26.dox.txt
I'll have a couple beers to this tonight :)
Page rendered at Monday, October 13, 2008 11:54:24 AM UTC
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.