Thinking about things such as optimal solution structure might seem insignificant at first. Why not to just add a few projects to the solution and forget about it?! The answer is sure, why not… don’t regret though spending lots of your precious time when your project will grow and require closer attention - exactly at the time when one mindful architect would start thinking about continuous integration. Here is the quote from Wikipedia that we will keep in mind:

“Continuous integration – the practice of frequently integrating one’s new or changed code with the existing code repository – should occur frequently enough that no intervening window remains between commit and build, and such that no errors can arise without developers noticing them and correcting them immediately.” – Wikipedia

Now, let’s agree on what do we really want to achieve step-by-step. First thing’s first:

  1. Integration of unit (and integration) tests into the build process
  2. Seamless integration with build server
  3. Management of version numbers
  4. Integration with NuGet galleries (repositories)
  5. And finally, enabling the continuous integration (isolated to the current solution)

I would like to stress that based on my professional experience it’s extremely important to bring some order into the structure of your solution before going into solving any of the mentioned points towards continuous integration.

Tool chain

It’s worth mentioning which tools am I going to be relying on. Most of them are standard ones starting from source control system to command-line shell:

  • Development environment - Visual Studio 201213
  • Programming language - C#
  • Source control system - Team Foundation
  • Build system - MSBuild
  • Command-line shell & automation scripting - Batch, Powershell
  • Packaging system - NuGet

Integration of unit (and integration) tests into the build process

Solution structure

Let us start with the structure of your solution - projects, test projects, additional files. Here you can see an example of the solution with a little bit of an order in it. Here are a few points of reasoning behind this structure: test projects are isolated in it’s own virtual (and physical) folder called Tests (meaning that both *.IntegrationTests and *.UnitTests projects are located under the \Tests folder). Going forward it will help to automate running all tests during build process. Test projects are split into two groups: unit and integration tests. There might be different views on this subject. Here is mine: unit tests project contains only tests that do not have explicit side effects like databases, files or web services that has to be there for such tests to run. One could think of unit tests as tests that can be executed on the build server without having additional setup arranged for it. On the other side integration tests project contains such tests that might require additional setup like databases, files, etc. Integration tests are typically slower to run and normally less reliable (due to it’s dependency to external sources).

Projects themselves are located right under the solution node in this example. And I have to mention that strictly speaking it does not matter how you would arrange these projects.

Let’s now put this new structure of our solution into use. First of all we will try to find a more or less natural way of controlling any build process with Visual Studio. That is MSBuild. Here is an example of Build2012.proj script for MSBuild (2012 refers to the version of Visual Studio used). Let’s go through some logical parts in it.

First there is a definition of some properties: project name, path to MSTest.exe and both unit and integration test containers (paths to respective assemblies). Then there is the series of actions dependent on each other so that if at least one of them fails - whole build fails. Here are those in order of execution:

  • BuildDebug
  • RunUnitTests
  • RunIntegrationTests
  • BuildRelease

Now, let’s see how it’s possible to compile the solution with simple commands (just make sure that MSBuild.exe is in your %PATH% variable):

1
msbuild .\Build2012.proj /target:BuildRelease

According to the default verbosity level of the output you should see rather large output being produced:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.18051]
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 07.08.2013 11:59:23 PM.
Project "...\Build2012.proj" on node 1 (BuildRelease target(s)).

BuildDebug:

###############################################################
# Building in DEBUG mode
###############################################################
...
###############################################################
# Running Unit Tests
###############################################################
...
###############################################################
# Running Integration Tests
###############################################################
...
###############################################################
# Building in RELEASE mode
###############################################################
...
###############################################################
# RELEASE build is now complete!
###############################################################

Done Building Project "...\Build2012.proj" (BuildRelease target(s)).

Build succeeded. 0 Warning(s) 0 Error(s)

Time Elapsed 00:00:12.22

If you would define BuildDebug as a target, building process will end on building in debug mode, etc.

Going forward this Build2012.proj script can be used as the key script on the build server. It can and should also be used in automation of building your solutions in general. Since this script is generic and only dependent on the structure of the solution (where to find test projects) it can be used for virtually any solution.

Management of version numbers

Version management is another point towards continuous integration. Not strictly required though. In most cases version is controlled with assembly level attributes in AssemblyInfo.cs files and it might be handy to have a script changing it in a controlled way for you. Here you can find an example such as this powershell script: IncrementVersion.ps1. This script is capable of locating all files that needs to be changed, checking it out, changing and prompt user with commit dialog with filled comment field waiting for you to accept it or reject. Here is how it can be used (remember that there should be no pending changes to have it do the job).

1
.\IncrementVersion.ps1 -Revision

Depending on the parameter this script will increment major version (minor, revision and build versions will be set to zero), minor version (revision and build will be set to zero), revision version (only build will be set to zero) or a build version.

Integration with NuGet galleries (repositories)

NuGet is a free and open source package manager for the .NET Framework. NuGet is distributed as a Visual Studio extension, and integrated with SharpDevelop, and is included in the C# code snippet tool LINQPad. NuGet can be used from command line and automated via scripts. NuGet supports native packages written in C++. – Wikipedia

Package manager is a collection of software tools to automate the process of installing, upgrading, configuring, and removing software packages for a computer’s operating system in a consistent manner. – Wikipedia

There is an excellent article available about NuGet: Managing Dependencies with NuGet. It covers the dependency management problem, installing and using NuGet.

The idea is to be able to control the deployment of libraries. Yes, only libraries. There is a version of NuGet-based package manager available on Windows platform for applications called Chocolatey (that is trying to conceptually bring aptitude paradigm from linux world). It has not been yet released and has lots of limitations. I would still like to encourage you to try it out.

Here is the script that takes care of packaging libraries that has NuGet specification files and deploying it to supplied NuGet repository: DeployToNuGetRepository.ps1.

Documentation for NuGet specification files can be found here. Here is an example of such a specification for the ClassLibrary1 project in our example solution which does not have any dependencies other than standard framework assemblies:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <authors>$author$</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <tags>common</tags>
    <language>en-US</language>
  </metadata>
</package>

Example of NuGet specification file for ClassLibrary2 project that has a dependency on ClassLibrary1 will look like following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <authors>$author$</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <tags>common</tags>
    <language>en-US</language>
    <dependencies>
      <dependency id="ClassLibrary1" version="$version$" />
    </dependencies>
  </metadata>
</package>

In order to initiate the deployment (after solution was build in release mode) the following command should be invoked:

1
.\DeployToNuGetRepository.ps1 -Target:"D:\Repositories"

This will deploy packages of our libraries into NuGet repository. These libraries can then be redistributed with using NuGet facilities. And will produce the following result:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Packing all available NuGet packages (*.nuspec) into gallery: D:\Repositories

[ PACKING: ...\OptimizedSolution\ClassLibrary1\ClassLibrary1.csproj ]

Attempting to build package from 'ClassLibrary1.csproj'.
Packing files from '...\OptimizedSolution\ClassLibrary1\bin\Release'.
Using 'ClassLibrary1.nuspec' for metadata.
Successfully created package 'D:\Repositories\ClassLibrary1.1.0.0.0.nupkg'.

[ PACKING: ...\OptimizedSolution\ClassLibrary2\ClassLibrary2.csproj ]

Attempting to build package from 'ClassLibrary2.csproj'.
Packing files from '...\OptimizedSolution\ClassLibrary2\bin\Release'.
Using 'ClassLibrary2.nuspec' for metadata.
Successfully created package 'D:\Repositories\ClassLibrary2.1.0.0.0.nupkg'.

Enabling the continuous integration

It’s a bit far to go yet for a strong continuous integration setup. But steps we went through can hopefully help getting there.

Downloads