Writing a Unit Test Action
This tutorial will show you how to build a simple PHPUnit unit test extension using the BuildMaster SDK from scratch in C#. This tutorial assumes that you're using Visual Studio 2010 with NuGet installed. If you are using a version of Visual Studio that doesn't support NuGet (i.e. any version prior), you'll have to copy over and manually import the BuildMaster Core DLL (i.e. BuildMasterCore.dll) from an existing BuildMaster installation (v2.5.6 or later) and add it to your project. See the NuGet FAQ for more details on what is required to install NuGet.
This tutorial will not show you how to create a custom editor for your extension since it generally is not required.
Creating the Project
BuildMaster runs on .NET 2.0, which means you can also write your extension targeting .NET 3.5 if you know the platform the extension will eventually be executed/run on has .NET 3.5 installed on it. For this example, I will target the 2.0 version of the framework since I won't be needing the extra features included with version 3.5.
Select File > New > Project... in Visual Studio, then Visual C# > Windows > Class Library. I'll change the target framework to 2.0, select a path, and choose OK when I have done this.
Getting the SDK
If you've already installed the NuGet extension to Visual Studio, it's easy to get the SDK files you'll need to create a BuildMaster extension. Select Project > Manage NuGet Packages.... In the NuGet extension search box, enter BuildMaster and you should find the SDK:
If you select Install, NuGet will download the required BuildMasterCore.dll directly to your project and you should see it in the References folder of the solution:
Creating the Extension
According to the SDK documentation, I'll need a BuildMasterAssembly assembly attribute added somewhere to the project. I'll put this in AssemblyInfo.cs,update its details, and remove some of the unnecessary attributes for this example:
Now that the attribute has been added, I'll rename Class1 to something more useful (i.e. PHPUnitTestAction), then inherit from UnitTestActionBase and implement its abstract members:
Now we'll need to add the properties of the action which will maintain the persistent data of the extension once it is saved. Since unit test actions all wrap a command line tool, I'll need to see what sort of options are provided by the PHPUnit executable with phpunit -help. For this extension, the only specified argument I will use is the UnitTest command (which I'll call TestToRun). I'll also add a property for any additional arguments so consumers of the extension can customize the test runner to their needs. Note that each property I add will be decorated with a Persistent attribute. This attribute tells the persistence engine to automatically serialize these properties to the database so that they can be recalled when the action is sent over the wire to be run on a remote server:
With our properties in place, we can begin writing the foundation of the action. We'll start by implementing the ToString method. This override will be displayed on any page with a deployment plan description, so it should describe the action's current configuration based on the properties:
Before we can write the logic to parse test results, we'll need to set up some configuration details for this provider. We'll use a Configurer for this purpose. In BuildMaster, a Configurer is a way to configure an extension at the global level so you won't have to add the same property to every instance of an action. For this extension we'll add a configurer to determine the path of PHP.exe, and also the path to the phpunit script.
To do this, we'll add a class to the project called PHPUnitConfigurer that inherits from ExtensionConfigurerBase. We'll add two properties to it (one for each respective path) named PHPExecutablePath and PHPUnitScriptPath. The ToString override is not needed at this point and can be implemented by returning an empty string. Since we're only allowed one configurer per extension, we'll need to add an assembly attribute to denote which .NET type represents the configurer. The final result will look like this:
Now we can begin writing the logic used to gather and parse the test results. In the RunTests method, the goal is to call RecordResult for each individual test indicating whether it passed or not. Since we have our configurer set up, we will now have the paths as variables that we can refer to from the RunTests method.
The first step that we must take is running the test runner on the supplied TestToRun value. Since PHPUnit is a PHP script file itself, we'll have to run it as the first argument to the PHP executable. Since UnitTestActionBase inherits from CommandLineActionBase, we can easily call the ExecuteCommandLine method with the necessary arguments such that it will generate an XML file that we can parse.
If I run the PHPUnit script locally, I can see the output generated (because of the –log-junit switch) follows this format:
and any failures are denoted with a <failure> or <error> child element beneath a <testcase> element. Since <testsuite> elements can be nestled at any level, to get each individual test we'll have to select all <testcase> elements at any level below the root and see if they contain a child element that denotes an error. With a bit of XPath, this is fairly straightforward:
By calling RecordResult, the action will record an individual test result for display within BuildMaster.
Now that the action itself is complete, we just need to add some metadata to it so it is categorized correctly within BuildMaster. This will be accomplished by adding an ActionProperties attribute to the class:
We are now ready to package our extension and upload it into BuildMaster.
Packaging the Extension
Extensions are nothing more than an a .NET assembly inside a .bmx file (which is simply a .zip file with the extension renamed). Building the extension is performed similar to any other class library. In Visual Studio, select Build > Rebuild [Project Name]. If you navigate to the build output directory (by default \bin\Debug relative to the project), you should see your assembly there. All you need to do is create a .zip file, add your assembly to the .zip archive, and rename the archive with a .bmx extension. Now your extension is ready to be uploaded into BuildMaster!
Adding an Extension to BuildMaster
To add an extension to BuildMaster, select Admin on the top-right of the screen, then Extensions under Integration Configuration. This will display the list of extensions that are already loaded in the tool. If you scroll down, you'll see a button that will allow you to add a new extension:
Click Add New Extension, and then Browse - you will be asked to browse to the extension you wish to upload. Once you've selected your extension, select Add Extension:
Once the page refreshes, the extension will be added to your list:
If you notice, the PHPUnit extension I just created has a hyperlink. This link takes you to the configurer for the extension. Click that, and we'll set the values we required in code earlier so that the extension won't just throw an exception when it is used:
With these values set, it's time to use the extension!
Using the Extension
In my demonstration, I'll set up a deployment plan that:
- Pulls the latest development branch source code from phpBB3
- Runs the PHPUnit tests included with the source
- Transfers the bulletin board source to a local website
After you've created an action group, select Add New Action, select the Testing group (which we specified in the ActionProperties attribute), then you should see "Execute PHPUnit Tests" in the list:
I'll select my local server for the action, I'll be able to edit the action. Note the properties we marked with the Persistent attribute are listed at the bottom along with GroupName (a field that segregates the unit tests into different groups if desired, which is also persistent, only it's part of UnitTestActionBase):
I'll leave the fields blank because I want to run all tests and have no additional arguments to add. I don't need a group name since I'm just going to run them all.
When finished adding my actions, this will look like the following in BuildMaster:
If I create a build, I'll be able to see that the unit tests failed after the build execution is complete:
If I click [test results], I'll see the individual tests that failed:
And that's all there is to it! With the help of this extension, any user can add this action to any deployment plan to run PHP unit tests.