Building a Development Environment Part 4: make CruiseControl.NET execute and report your NUnit tests

This is part 4 in my series on managing the build process.

After seting up a source code repository in Part 1 , setup CruiseControl.NET to monitor it in Part 2 and actually produce some sourcecode in Part 3 we now will test this code to see if it works correctly.

That is what we will do in this part of the series: Create a simple library written in C# and build it automaticaly.

But first:

Disclaimer

This post is by no means “The Way” of building a development environment. In fact, you will notice that at times I deviate from some of the practices advocated by the referred articles at the end of my articles. It’s our interpretation of the process and how we implement it. If you have any remarks or suggestions for improvement, please post a comment.

Tooling used

Appart from the tooling of the previous articles, we also use

  • NUnit-2.2.8-net-2.0: This is used on the developer machines and on the CI server.

Setup the developer machine

First we will discuss how to organise the developer machine. First we will add our additional tooling and then we will add some folders which will be used while testing.

Adding NUnit

NUnit will be added to our Devenv subfolder of the developer’s workstation working folder. For this we unzip the content of the NUnit zip into a subfolder NUnit of the Devenv folder.

So we end up with following structure:

nunit.gif

Adding new folders

In our projects workingfolder we will add two folders:

  1. Log
  2. Unittest (this one was actually allready created in my previous article, but I ignored it then)

So, now we have following folder structure:

newfolders.gif

The folder named Unittest will be used to put the code for our unittests in, so we want it in our source code repository. The Log folder however will be used to store our test results and buildlogs. We do not want those in our repository.

So we tell Subversion to ignore this folder:

D:\Serge\Devenv\simulation\wks1\work>svn propset svn:ignore “Log” Project3

Create a unit test

We have created our library and a class with a method. Now, we will create a unit test to check our method.

Test Driven Development
I know !!!! The XP die hards will tell you to write your tests first and then your code. But I’m demonstrating the setup/development of an automated build system so I want to focus on performing some steps in the buildprocess. That is why I’ve split up the process in first creating a library of code, build it, add a test and test it.

Once all scripts have been developped, there is no one keeping you from implementing the XP order of things.

We create our unittest project in the solution file for our component. We save it however in a subfolder of the Unittest folder with the name of our module extended with Unittest. The name of this project is the name of the module tested by it, extended with Unittest.

So we get the following solution structure in Visual Studio:

solutionwithunittest.gif

And the corresponding folder structure in our working folder:

workingfolderwithunittestv3.gif

Build and Test it

We will of course first build our tests and then execute them. And we want to do this automatically.

Prepare it

To be able to build our tests and eventually execute them, we need to make our build environment aware of:

  • where our tests are
  • where our test execution program is

For this, we will add two properties to our buildenv.build file:

<?xml version=1.0?>

<project name=Basic settings>

  <description>Contains the basic settings for

    an installation of the PVS build environment.

    </description>

 

  <!– Other properties here (see previous articles)–>

  <property name=subdir.unittest

            value=Unittest\ />

 

  <!– Other programs here (see previous articles)–>

  <property name=dir.nunit

            value=${environment::get-variable(‘BUILDENV_WKS_WORKFOLDER’)}Devenv\NUnit\ />

</project>

Now that Nant knows where to find everything, it also needs to know how to build and execute the tests. Which is why we add following target tasks to our defaulttasks.build file:

  <target name=producttest

          description=build the core project>

    <foreach item=String

            in=${product.components}

            delim=,

            property=component.name>

      <echo message=testing component ‘${component.name} ${product.configuration}’ />

      <property name=component.buildfile

                value=${project.basefolder}${subdir.product}${product.name}\${component.name}.build />

      <nant buildfile=${component.buildfile}

            target=testproject />

    </foreach>

  </target>

  <target name=componentunittestbuild

          description=build the unittest project>

    <foreach item=String

            in=${component.modules}

            delim=,

            property=component.module.name>

      <echo message=building module ‘${component.module.name}Unittest ${component.configuration}’ – logfile:${project.basefolder}${subdir.log}${component.name}\${component.module.name}Unittest.${component.configuration}.log />

      <property name=devenv.commandline

                value=“${component.dir}\${component.name}.sln” /build ${component.configuration}  /project “${componentunittest.dir}${component.module.name}Unittest\${component.module.name}Unittest.csproj” /projectconfig ${component.configuration}  /OUT “${project.basefolder}${subdir.log}${component.name}\${component.module.name}Unittest.${component.configuration}.log” />

      <echo message=${devenv.commandline} />

      <exec program=devenv.exe

          basedir=${dir.msvsnet}

          commandline=${devenv.commandline} />

    </foreach>

  </target>

  <target name=componentunittestexec

          description=execute the unittest>

    <foreach item=String

            in=${component.modules}

            delim=,

            property=component.module.name>

      <echo message=testing module ‘${component.module.name}Unittest ${component.configuration}’ />

      <property name=testenv.commandline

                value=${componentunittest.dir}\${component.module.name}Unittest\bin\${component.configuration}\${component.module.name}Unittest.dll /xml:${project.basefolder}${subdir.log}${component.name}.${component.module.name}Unittest.${component.configuration}.Testresults.xml /nologo />

      <exec program=nunit-console.exe

          basedir=${dir.nunit}bin\

          commandline=${testenv.commandline} />

    </foreach>

  </target>

Build and Execute it

The buildfile for our product gets some more tasks too.
We add a general task which will execute the complete build process, that is:

  • Build our core code
  • Build our tests
  • Execute our tests
  • In the future, execute other tasks like generating documentation, other

We make a task which depends on all other tasks and therefore, when executed, will automatically execute all other tasks. We also make this the default tasks of our build file. We also make a task which executes our tests.

So, this is what our new SomeProduct.build file looks like:

<?xml version=1.0?>

<project name=SomeProduct basedir=. default=executeall>

  <description>Buildfile for SomeProduct.

    </description>

  <include

    buildfile=..\..\..\Devenv\buildenv.build />

  <!– Edit following properties for your project –>

  <property name=product.name

            value=SomeProduct />

  <property name=product.configuration

            value=debug />

  <property name=product.components

            value=Component1 />

  <!– End editable area –>

 

  <target name=executeall depends=buildproduct, testproduct />

  <target name=buildproduct>

    <nant

      buildfile=${dir.buildenv}defaulttasks.build

      target=productbuild />

  </target>

  <target name=testproduct>

    <nant

      buildfile=${dir.buildenv}defaulttasks.build

      target=producttest />

  </target>

</project>

And finally we add a property to the component buildfile which tells Nant the folder of our unittest project and a task which tells Nant for which modules to execute the tests:

<?xml version=1.0?>

<project name=Component1 basedir=.>

  <description>Buildfile for Component1.

    </description>

  <!– See previous posts for other properties –>

  <property name=componentunittest.dir

            value=${path::get-full-path(‘./../../’)}${subdir.unittest} />

 

  <!– See previous posts for other targets –>

  <target name=testproject>

    <nant buildfile=${dir.buildenv}defaulttasks.build

        target=componentunittestexec />

  </target>

</project>

The final change we make is the build launch batch file. Instead of executing the “buildproduct” target, we now make it execute the “executeall” target. Mind however that, because we set the “executeall” target as the default target of our build file, we could ommit the commandline parameter specifying the target and simply execute Nant in the product folder. But for now, we will still specify our target:

START /B ./../../../Devenv/Nant/bin/nant.exe -buildfile:SomeProduct.build executeall

Now, open a DOS window in the products folder and run SomeProduct.bat. You should see some Nant activity and finally the conclusion BUILD SUCCEEDED.

Setup CruiseControl.NET

We will have to replace the defaulttasks.build file in the devenv subfolder of the buildserver’s project folder with the above one, in which we added the new targets. We also will need to add the new properties to the buildenv.build file.

Check-in our changes

Some preparation

Before we commit our changes to our repository, make sure you have two programs running:

  • The Subversion server: we need this one to be able to check in our sources
  • The CruiseControl.NET server: we need this one to monitor our checkins and perform an automatic build

Save it!

Again, choose the TortoiseSVN > Add… submenu on the developers workstation project folder (see Part 3) and make sure to exclude the newly created “Log” folder and the modules “bin” and “obj” folders.

Next, use the Subversion exclusion feature to permanently exclude to above folders.

Finally select the Commit option on the project subfolder to check-in all our changes

Automated Build

If everything went well, you should again see a succeeded build on your projects website, but also see the results of the test.

For this, you open your webbrowser and go to (at least on my computer) http://localhost/ccnet/ select the project and then select the most recent build in the “Recent Builds” widget. If you then select “NUnit Details” you should see someting like the following:

citest.gif

Conclusion

In the previous article we checked in some code and build it automatically. In this article we added tests and build them and executed them.

Resources

[1] NUnit
[2] Integrating NUnit into your build in the CruiseControl.NET Server

Updates

11 December 2006: original version
10 januari 2007: made it correspond with the changes made in the previous articles.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s