Continuous Integration Part 4 : Unit Testing

by Mathieu 14. June 2008 19:09

This post is quite simplistic about unit testing, but I hope it will make you try ! Simply put, an unit test is a piece of code that will test another piece of code. The "unit" part means that we will test an "unit" of our code, like a class and its methods. Unit tests are runned with NUnit (http://www.nunit.org).

Lets take a "simple" method :

public class StringUtil 
{ 
    public static string GetLength( string input ) 
    { 
        return input.Length; 
    }
}

The test will look like this (add a reference to nunit.framework.dll) :

[TestFixture] 
public class StringUtilTest 
{ 
    [Test] 
    public void GetLenghTest() 
    { 
        Assert.That( StringUtil.GetLength( "ab" ) == 2 ); 
        Assert.That( StringUtil.GetLength( "abc" ) == 3 ); 
        Assert.That( StringUtil.GetLength( "abcd" ) == 4 ); 
    } 
} 

This test checks the internals of our method : given known input parameters ("abc"), we expect a specific output (3). Now, our method looks correct, and we have 100% coverage for it. But 100% coverage doesn't mean 0% bugs. Unit testing cannot test non existing code, like missing error checks, for example.

Now, an user submits a bug : "when I pass null to "GetLength", it fails with a NullReferenceException!". We now have two options : we return 0 if null is passed, or we throw an ArgumentNullException. We'll implement option 2. But before updating or GetLength method, we should add another test :

[TestFixture] 
public class StringUtilTest 
{ 
    [Test] 
    public void GetLenghtTest() 
    { 
        Assert.That( StringUtil.GetLength( "ab" ) == 2 ); 
        Assert.That( StringUtil.GetLength( "abc" ) == 3 ); 
        Assert.That( StringUtil.GetLength( "abcd" ) == 4 ); 
    } 

    [Test] 
    [ExpectedException(typeof(ArgumentNullException))] 
    public void GetLenghtArgNulTest 
    { 
        StringUtil.GetLength( null ); 
    } 
} 

If we launch tests, "GetLengthArgNulTest" will fail, as we're expecting an ArgumentNullException, and we get a NullReferenceException. We leverage unit tests to reproduce the bug, before correcting it. It ensures that when the bug is corrected, it stays corrected. And we improve our state coverage.

public class StringUtil 
{ 
    public static string GetLength( string input ) 
    { 
        if( input == null ) throw new ArgumentNullException( "Please dont feed me with null!"); 
        return input.Length; 
    } 
} 

Everything is now green in NUnit!

Now, why are we writing this bunch of code, comment and maintain it ? For fiability, and future maintenance of our application : a modification in a portion of test covered code is much safer than the same modification in uncovered code.

In a multi layered application, you can test layers independantly. You don't need to go to all UI features to test your access layer. Finally, testable code is more modular, as modularity improves testability.

kick it on DotNetKicks.com

Tags:

Continuous integration

Continuous Integration Part 3 : Integrating Microsoft Source Analyzer Tool to our build process

by Mathieu 5. June 2008 23:33

Two weeks ago, Microsoft released "Microsoft Source Analyzer Tool for C#" (I'll call it MSAT). This tool, also known as "StyleCop", enforces coding style rules against your source code. It is really useful when working in teams : everyone has to follow the same coding style, making source understanding easier for everyone.

Installing

MSAT can be downloaded here : http://code.msdn.microsoft.com/sourceanalysis/Release/ProjectReleases.aspx?ReleaseId=1047. Installation is straightforward, just be sure to have the MSBuild checkbox checked, or there won't be any MSBuild integration available.

Integrating in your projects

When you have installed MSAT, a new entry appears in the "Tools" menu inside Visual Studio : "Run Source Analysis". You can now launch source analysis inside Visual Studio, the results will be inside "Source Analysis" tab. Double-click a message, and you will go to the affected line. Nothing unusual!

You can also directly include source analysis in the build process. And that's pretty cool : every time a developper build the project, the analysis is run, and results displayed inside "Warning" section of the "Errors" tab. Now, there are two options :

- MSAT is installed on every developper's pc

For every project you wish to enable source analysis, open the .csproj file using a text editor, and edit it this way :

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
[...] 
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
<Import Project="$(ProgramFiles)\MSBuild\Microsoft\SourceAnalysis\v4.2\Microsoft.SourceAnalysis.targets" /> 
[...] 
</Project>

- MSAT isn't installed on every developper's PC

Following my project tree post, you can add MSAT folder inside the "External" folder of your solution : create a "SourceAnalysis" folder, and copy everything inside "C:\Program Files\Microsoft Source Analysis Tool for C#" into it. For every project you wish to enable source analysis, open the .csproj file using a text editor, and edit it this way :

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
[...] 
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
<Import Project="..\..\External\SourceAnalysis\Microsoft.SourceAnalysis.targets" /> 
[...] 
</Project>

Finally, add this directory ("SourceAnalysis") to the repository : every developper will get free Source Analysis on their next update !

Note : when you load your projects after modifying the .csproj file, a warning will appear : choose to load the project normally.

Et voilà! Source analysis is now integrated in our build process!

kick it on DotNetKicks.com

Tags: , ,

Continuous integration

Continuous Integration Part 2 : Solution tree

by Mathieu 5. June 2008 20:46

Before going further, it can be useful to have a look at a "standard" solution tree, that I will be using for upcoming posts :project_tree

  • Apps : applications : Winforms, Console, WebForms, everything that is executable
  • Libs : class libraries, module, components : everything that is not executable.
  • References : every external libraries used in your solution (NHibernate, Castle Project, etc)(not visible in Visual Studio, only in the "physical" directory)
  • Scripts : build scripts, deployments scripts
  • Tests : unit tests

Important : if you create solution folders inside Visual Studion you have to physical" folder, beware when you add projects to them. By default, Visual Studio adds them to the physical solution root dir. It's the same for solution elements, that are created inside solution root. It's safer to create solution elements manually, inside the desired folder, then add them to the solution by "Add existing element".

This solution tree is only for demonstration purpose. It's probably not the best, but it's not the worst either. It's always nice to have consistency between projects, and it simplifies greatly continuous integration process.

Regarding external libraries, I chose to link them on a "by solution" basis : it's simplier to make an update : you don't worry about breaking something, you can update your libraries solution by solution and test accordingly.

Tags: ,

Continuous integration

Continuous Integration Part 1 : Source Control made easy

by Mathieu 5. June 2008 20:29

This time, we'll see how to install and use Subversion. You will have to download VisualSVN Server (http://www.visualsvn.com/server/) and TortoiseSVN (http://tortoisesvn.tigris.org/). You can also download AnkhSVN (http://ankhsvn.open.collab.net/) if you want Visual Studio integration (for Visual Studio 2008, take version 1.0.3 or later).

VisualSVN Server, TortoiseSVN and AnkhSVN installation are straightforward. When VisualSVN Server is installed on our server, and TortoiseSVN on our client(s), we can begin.

During VisualSVN Server installation, you just have to remember the path labeled "Repositories location" for backup purposes.

Server side

First, you need to create a place where source code will be stored : a repository. Every modification of the code in the repository (commit) will increment its version number. Repositories are created in the repositories path specified during VisualSVN Server installation (c:\repositories by default). This is the only directory you need to backup. If you restore this directory on another server, install VisualSVN Server, everything (repositories, source code, users and groups) is back to normal!

Now you can launch "VisualSVN Server Manager", and "Create a repository". You can name your repository after your project's name. Do not forget to leave "Create default structure" checked : it will create 3 directories, at your repository's root :

  • trunk : development version of your source code
  • tags : specific (released) revisions of source code
  • branches : development duplications of trunk, for adding new features that don't belong to the trunk at this point of time.

You can have a look at this post from Bill Simser who explains very well those concepts.

Now we have a repository, let's add some access control. You can create your developpers by clicking on "Create new user", and create your groups with "Create new group". You can associate users into groups. Finally, to define access rules, right click on your repository in the treeview, and choose "Security".

Before going client side, remember your repository's url, like http://server:8080/svn/RepositoryName.

Client side

Now that we have a repository, we need to create a local copy of it. Every modification made on the local copy is made on the repository, after you "commit" it. Every modification on the repository is made on local copy by "updating" it.

You can create a folder on your development machine (like d:\ContinuousIntegration), right click on it and choose "SVN Checkout". Now enter your repository's url, appended by "trunk" : http://server:8080/svn/RepositoryName/trunk. Remember : trunk is your development branch.

Your local copy is ready. If you add files and folders inside, you just have to right-click, choose "Commit" and it's done (after a dialog), it's on the repository, with version tracking!

You can simplify some things, like never commit bin, obj, *.suo and *.user. You just have to right-click, TortoiseSVN -> Settings. Inside General, Subversion, fill "bin obj *.suo *.user" inside "Global ignore patterns". There is no use storing compilation products inside the repository : everything you need to make it is already there !

How to properly commit

1. Bob (our developper) updates his local copy, to get the latest version of the code 
2. Bob modifies the code
     2.1 If code doesn't compile, back to 2.
     2.2 If code compiles, go to 3.
3. Bob updates his local copy, to get modification that can have been made on the repository since 1.
     3.1 If code doesn't compile, back to 2.
     3.2 If code compiles, go to 4.
4. Bob commits his modifications.

Point 3 is very important : it ensures that Bob's modifications don't conflict with code in the repository. Which leads us to the most important point : the code in the repository must compile. Everytime. If someone updates his local copy with code that doesn't compile, he will be losing valuable time : who commited the code, why doesn't it compile, how to correct...

Tags: ,

Continuous integration

Continuous integration Introduction

by Mathieu 3. June 2008 20:49

Currently translating from my french blog http://www.cogimator.net, a small group of posts about continuous integration in .net.

Those posts will cover :

Tags: ,

Continuous integration

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen