devxlogo

Speed Up Project Delivery with Repeatability

Speed Up Project Delivery with Repeatability

very environment has them: The dreaded manual tasks that drain productivity from the team and add instability to the processes. We usually dedicate only half our brain power?and never enough time?to deal with them, which only compounds the problem. But what if you could automate the most painful tasks and gain a huge boost in productivity and speed of delivery?

Anyone who has worked on a large project, especially one with multiple developers, knows the pain of dealing with code change conflicts, the “works on my machine!” syndrome, “magic” builds, and other process and procedure anti-patterns. Developers hate having to deal with these things, nonetheless, we always get stuck having to do them. In this article, I’ll walk you step-by-step through some simple changes you can make to your environment to eliminate many of the non-code related headaches. By producing reliable, quality builds every time and easing the pain of deployment to multiple environments you’ll be able to get back to doing what you really love: Coding!

Author’s Note: All the tools used in this article are free and most are open source. You can try out these techniques with no startup cost other than your time. All the tools have active communities with mailing lists, wikis, and websites from which you can learn more, ask questions, and even contribute. If you find any of these tools useful, please consider donating some time and contributing some documentation, FAQ answers, or tutorials back to the community to help make the tools even better!

Keep Your Tools Sharp

Raise your hand if you love preparing a release build for production development or release-to-customer. If you didn’t raise your hand, you’re like most people involved in software projects. Sure, you know that there are many new and old tools that help you automate these things, but it takes time to get up to speed on them and use them correctly. It’s an old problem. Stephen Covey’s book The Seven Habits of Highly Effective People uses a really great tree-chopping analogy for this concept: “I don’t have time to sharpen the saw, I’m busy sawing!” If you don’t take the time to sharpen your saw, eventually the growing inefficiency of dull tools and over-work will choke your productivity. Time invested in keeping your tools sharp can pay back handsomely.

Remove Friction and Surprises

Repeatability is a virtue. It’s both something you strive for and a goal to be achieved. You should try to identify friction/pain points and try to automate them as soon as possible. One fact I have observed in my projects is that automating a frequently-repeated task is always worthwhile. The time it takes to automate a task is usually recaptured after the third or fourth time the task is repeated. So the ROI happens extremely quickly. The key to automating these tasks, however, is to have the right tools and know how to use them.

In summary, you should strive to eliminate all the “magic” in your software process. If you can perform a task on one developer’s box, you should be able to do it on any developer’s box. You should be able to eliminate all surprises as quickly as possible, and automate all manual tasks in the critical path so you can mitigate risk to the maximum extent possible.

Key Aspects of Repeatability

This article covers five key aspects of a repeatable environment:

  1. Source control
  2. Automated build
  3. Automated testing
  4. Automated deployment
  5. Continuous integration
Author’s Note: Previous issues of CoDe Magazine have covered many of these topics, and you’ll find links to those articles here, so that you can delve deeper into that particular subject.

Editor’s Note: This article was first published in the November/December 2009 issue of CoDe Magazine, and is reprinted here by permission.

Source Control

Takeaway: Code should have its primary home in a central, well-known location. The point of source control is to eliminate the guesswork of who has the most recent source code files. This is especially crucial on a project involving multiple developers, as the situation can quickly get out of hand. Eliminate the waste involved in having to manually “diff” files or look at last-modified dates to try to figure out which version of a given source code file is the correct one. Eventually, you’ll move on to other projects and someone else will have to maintain this code. Will they be able to find it and feel confident that they have the code that matches what’s actually deployed? Source control helps address all these problems.

Several good offerings are on the market today for source control management, including both free/open-source and commercial offerings. Several of the more common/popular ones include: Microsoft Visual SourceSafe, Visual Studio Team System Team Foundation Server, and Subversion (open source). Personally, I like Subversion, so this article uses a Subversion example for instituting source control.

A Quick Introduction to Subversion

Rick Strahl covered Subversion in his July/August 2008 CoDe Magazine article. If you’re coming from a Visual SourceSafe background, you’ll find Subversion’s mode of operation is a little different. SourceSafe uses a library approach (i.e., “lock, edit, unlock”) mode. Subversion uses a collaborative approach (i.e., “copy, edit, merge”). Using Subversion, you simply retrieve the latest working copy from the server and then just start working on that local copy. When ready, you commit your changes to the repository. Subversion will merge your changes with other people’s changes. If Subversion encounters any trouble, it will ask for your help. In my experience, this is rare, but when it does happen, there are powerful tools such as the TortoiseSVN GUI client (more on that later) that can help you deal with merge conflicts easily. Compared to the alternative (having to shout across the cube farm to tell Frank to unlock a file you need), merge conflicts are a small price to pay for all the other functionality in Subversion.

Setting Up a Subversion Server and Repository

The fastest way to get a Subversion repository up and running is to download the free VisualSVN Server product from the folks that also make the VisualSVN product (a commercial Visual Studio add-in for Subversion interaction).

Install VisualSVN Server on a server or other computer that will be highly available. Don’t install the Subversion repository on your developer workstation, because it will hammer disk performance when clients are performing operations?not to mention the problems created if you have to reboot or shut down. You should also set up backups of your SVN repository. This can be as simple as zipping up the repository folder and copying it to another computer or doing a proper backup according to your company’s IT backup policy.

After setting Subversion up, you can add users. I wouldn’t get too bogged down in configuring their access permissions at this point. That can come later when you’re more familiar with the system and your usage patterns.

Installing VisualSVN Server

After downloading the VisualSVN setup package (MSI), run the installer and accept the license agreement. You’ll need to enter some important settings (see Figure 1), including the root path for your repositories, the HTTP/HTTPS port number, and the type of authentication you wish to use.

?
Figure 1. Visual SVN Server Options: After you run the installer, you’ll see the installer options screen, which requests some settings.
  • Location: This is the location to which the VisualSVN server itself will be installed. I recommend leaving this as the default.
  • Repositories: This is the folder into which all your repositories will go (you can have many). I recommend putting this on a separate drive if you have one depending on your performance needs and number of developers using the server.
  • Server Port and SSL: Change this if you need to avoid port number conflicts. I recommend leaving this default and using a secure connection. VisualSVN will generate an SSL certificate for you to use internally so you don’t have to purchase a full SSL certificate for public use if you don’t need to.
  • Authentication: VisualSVN Server and Subversion can maintain their own user database, complete with hashed passwords. This is probably the easiest/simplest thing to do to get going quickly. If your organization requires Windows authentication (or you prefer it), use that option instead.

After installing the server, open the VisualSVN server manager (see Figure 2) to configure your repositories, user security, and other options (no config-file-munging required, unless you really want to).

To create your first repository, right-click on the “Repositories” element in the tree view, and choose the “Create New Repository” option. For the purposes of this article, I called mine MyFirstRepository. Note that as you type the repository name in the text box, the URL of your repository appears below (see Figure 3). Before you click OK, you may want to jot this down or copy it to the clipboard for use in a little bit. Next, add users and groups as appropriate. For now, it may be easier to just create a single user that has access to your new repository. While you’re here, you may want to create a user called “TeamCityUser” for use later if you configure a Continuous Integration server. Beyond that, you can get fancier with user accounts later. For now, let’s proceed with the basic setup.

?
Figure 2. VisualSVN Server Manager: Use the Server Manager to configure repositories, security, etc.
?
Figure 3. VisualSVN Server New Repository Dialog: You’ll see this dialog when creating a new repository.

Now that you have a server set up as well as a new repository and associated URL, you can connect a client and start adding code!

Setting Up the Subversion Clients

On your developer workstations, I recommend installing the TortoiseSVN Subversion client for Windows. It integrates with Windows Explorer and works really well. If you prefer a source control workflow integrated into your Visual Studio IDE, you can use the free, open source AnkhSVN or the commercial VisualSVN.

To keep things simple for this article, I’ll use TortoiseSVN as the client, but I strongly recommend you check out Ankh and VisualSVN when you get a chance.

First, on your developer workstation, download and install TortoiseSVN from the link above. Next, decide where on your file system you want the code to reside. Let’s say you chose C:Code. In Windows Explorer, create a sub-folder called Author’s Note: If you’ve used Visual SourceSafe previously, the term “Checkout” in Subversion parlance, is close to the concept of “Get Latest Version” in VSS. When you’re initially getting the source from a Subversion server, use the “Checkout” command.

?
Figure 4. TortoiseSVN Initial Checkout: You need to know the repository URL, and specify a directory to place checked-out files.

Next, you will be prompted to enter the repository URL and a few other options. This is where the repository URL of the repository you just created will be necessary. See Figure 4 for an example.

Once you have filled in these options and clicked OK, and since this is the first time you’ve connected to this repository, TortoiseSVN will ask you whether you trust the SSL certificate for this server. I recommend you choose “Accept Permanently.” Next, you will be asked to enter your username and password. Use the credentials of the user you created above. You have an option to remember these credentials so you won’t have to be prompted for them every time. I recommend doing this as having to type in your login and password every time you touch the repository can be very annoying.

At this point, Tortoise has everything it needs to connect to the repository and perform a checkout. You should see a screen similar to Figure 5. You’ve now set up the repository file system. You can now add, update, and delete code and other artifacts of your project.

Setting up a Global Ignore Pattern

?
Figure 5. TortoiseSVN Checkout Status Dialog: This status screen shows the status of files in your project.

One last thing you might want to do, before you start seriously using TortoiseSVN, is to set up your “ignore” list. By default, SVN allows you to put whatever files you want in the repository. There are several types of files that you probably don’t want in your repository at all. These include things such as compiled output (i.e., the inDebug folder), the Visual Studio user-specific .suo and .user files, and other machine-specific or transient-type files.

To do that, right-click on any folder and navigate to the TortoiseSVN menu, then choose “Settings.” You’ll see a dialog box. In the “Subversion” group, find the textbox labeled “Global ignore pattern.” Enter your ignore pattern(s) here. As an example, the snippet below shows the ignore pattern I use. Feel free to customize it for your environment.

   Bin */Bin bin */bin obj */obj *.suo *.csproj.user    *.log *.log.* *.rdl.data *.rptproj.user    _ReSharper* *.resharper.user build */build

The preceding ignore pattern filters out all the files in binDebug, binRelease, and obj folders, as well as temporary log files, user settings, the ReSharper (if you use ReSharper) cache folder, and any “build” products you may have, preventing TortoiseSVN from storing those unwanted files in the repository.

Workflow of a Subversion User: The Check-in Dance

Now that you have the repository up and running and you have a local folder, here’s how to add some code. Assume you already have an existing project. Create a folder under the trunk folder called source and add your existing code folder(s) under there. Right-click on the source folder and click “Add” to add all your existing files, and then right-click on source and choose “Commit.” Type in a message, and then click OK. That sends your first code commit to the repository. The code you committed is the code that other developers will get when they connect their clients and perform an “SVN Checkout.” Congratulations! Now your code is in a repository. This is a first, major step towards adding repeatability to your process.

A Subversion user’s typical day goes something like this (colloquially known as the “Check-in Dance”):

  1. Start work, perform an “Update”: Right-click on your “trunk” folder in Windows Explorer and choose “SVN Update.” TortoiseSVN will now retrieve any updates that other developers have committed since you last updated.
  2. Resolve any conflicts and review any interesting changes: In the TortoiseSVN update status window, you will see a list of files that have changed and their disposition (updated, merged, conflict, added, deleted, etc). If, for some reason, Subversion could not merge one or more changes from other users into your code tree, you will have an opportunity to resolve the conflict. If you know their version is correct, you can right-click on the conflicted file and choose “Resolve conflict using theirs.” If you’re sure your version is correct, you can choose “Resolve conflict using mine.” If there’s no clear answer, select “Edit Conflicts” to use the TortoiseMerge tool to perform a manual merge. This is usually not a pleasant task, but the tools in Tortoise make them relatively simple.

    Of course, you can always manually merge the file in your favorite text editor/IDE, but the visual cues in TortoiseMerge can be compelling for most circumstances (see Figure 6).

    Merge problems can sometimes occur if you make changes locally to the code during a refactoring, but haven’t yet committed the change and another user made similar changes.

    In addition to managing conflicts, you can also right-click on files that changed and see the differences in Tortoise’s Visual Diff Viewer application.

  3. ?
    Figure 6. TortoiseMerge: Use the TortoiseMerge application to help handle change conflicts.
  4. Start making changes: After integrating changes into your local copy, you can start coding normally?that is, you should code as though no source control were in place. Add, remove, and change files as necessary; don’t worry about locking or unlocking files, etc.
  5. Update to integrate the latest code: When you complete your code changes and are ready to commit them to the source control repository, you should do one more update just in case there are any late-breaking changes that you should be aware of that might cause an integration issue with other developers. Subversion will notify you that other people have committed to the repository since the last time you updated, and in most cases will not allow you to commit until you’ve done another update. After you’ve updated and resolved any conflicts, you should consider running any test suites or verification programs you may have set up in your process.
  6. Commit your changes: Finally, when you’re ready to commit your changes, right-click on your trunk folder in Windows Explorer (or you can use the IDE-integrated experience if you’re using the VisualSVN or AnkhSVN Visual Studio plug-ins) and choose “SVN Commit.” At this point, take a minute to look at the list of files that Subversion thinks should be committed. Make sure that any files that need to be added are marked as “added” and will be uploaded to the repository. Next, type in a descriptive message that describes what’s in this update you’re sending, and perhaps why the changes were being made. Trust me, these comments help you when you have to come back two months later and look at commit logs to figure out why you made a particular change to this file. When you’re ready to commit, click OK to start the commit upload process.
  7. Resume work: If you’re not yet done for the day, at this point you can resume working on your code and start the process over again.

For people not used to source control, this process may seem like a lot of extra work, but it’s not as hard as it sounds, and only rarely is it ever more than a minor annoyance. It’s far less painful and annoying than fighting for locks to a file or dealing with the “Jim went on vacation and left half the source tree locked? again” sort of problems you have with lock/edit/unlock style source control systems.

The Check-in Dance helps maintain a high level of integration among developers and also encourages a sort of “source hygiene” that discourages people from committing bad (i.e., build-breaking) changes that can slow down your team at the worst times.

Automated Build

Takeaway: The code should have an associated automated build script that requires no magic by the person running the script. Here’s a common scenario: A new developer joins your team. She gets her workstation all set up. She checks out the latest source from your source control repository and attempts to build?and nothing works. She’s missing this component, doesn’t have this environment variable set up, etc, etc. Here’s a related situation: You set up a test server to help build, test, and preview your software release packages and nothing seems to work because the server doesn’t have Visual Studio on it, or some SDK installed, or some DLL in the GAC.

Automating your build process helps remove a lot of the guesswork of building your project. You can include simple dependency checks to make sure that the system has the prerequisites for building your software installed, which will allow you to give humans a better indication as to why things are failing (for example, “Please install the .NET Framework 3.5 SDK”).

Automated builds also serve as documentation for how to build, assemble, and package your project for release. Should you win the lottery and quit tomorrow, the next person to take over after you will be able to get going quickly, without having to guess your software’s requirements.

I like the NAnt build tool for doing automated builds. NAnt uses a simple-format XML build file format that harkens back to MAKE days, but doesn’t have all those pesky whitespace problems. A typical NAnt build file has several “targets” that do common things, such as building/compiling a Visual Studio solution, running tests, generating documentation, packaging up the results, and putting them in a well-known location for people to grab and use.

Getting Started with NAnt

Before launching into this brief introduction to NAnt, David C. Bost’s article on NAnt goes into more detail, if you’re interested.

First, download the latest version of NAnt. At the time of this writing, the release is 0.86 Beta 1. Don’t be put off by the version-numbering scheme. Nant has been pre-version 1 since before Windows XP was released, yet NAnt has been one of the most reliable pieces of software I’ve ever used.

I recommend getting the bin ZIP package and grabbing the contents of the bin and schema folders. The docs and examples are there for your convenience, but the online versions are probably more up-to-date. Create a sub-folder of the runk folder in your source code tree; call it something like “tools” or “support.” Create a sub-folder of that called NAnt, and extract the bin folder from the NAnt ZIP package in that new NAnt folder.

The reason for keeping the NAnt binaries with your source trunk is that you may, at a later date, add custom tasks to NAnt or apply special configuration changes that are appropriate to build your project. These changes usually end up being tied to specific versions of the software and so it’s beneficial to keep both the NAnt version and project-specific modifications together. It takes a little extra space, but is usually worthwhile.

Remember the schema file I told you to grab from the NAnt ZIP package? Place the XSD file located inside that folder into your Visual Studio XML Schemas folder. That enables IntelliSense when you’re editing NAnt build files in Visual Studio. Intellisense comes in very handy?especially at the beginning of the project when you’re doing a lot of build file hacking.

For Visual Studio 2005 and 2008, copy the XSD file to C:Program FilesMicrosoft Visual Studio 8.0XmlSchemas or C:Program FilesMicrosoft Visual Studio 9.0XmlSchemas, respectively, where C: is your system root drive. Note that if you’re using a 64-bit version of Windows, you will probably have to use Program Files (x86) instead of Program Files.

Now you have an installed copy of NAnt with Visual Studio support. Now you can create your first NAnt build file.

Creating the NAnt Build File

Create a new empty XML file in your runk folder called build.xml. The specific name of this file isn’t terribly important, because you can tell NAnt what the file name is, (by default it’ll have the name default.build) but it helps when editing in Visual Studio if the file name ends with XML.

Next, create a simple batch file called build.bat that executes the NAnt executable with the build file and any optional parameters:

   @ECHO OFF   .	oolsNAnt
ant.exe -nologo /f:build.xml %*

Using that batch file, from a command-prompt whose current working directory is your runk folder, you can simply type “build” to execute NAnt with your build script. You can also pass in any optional arguments to your build script, allowing you to run a “release” build, or a “debug” build, etc.

Next, you’ll add the first parts of the build file to get a simple, working build. The most basic NAnt build script file has the XML declaration and the root element:

         

In the project root element, the name attribute is useful for documentation purposes. The basedir attribute value of “.” tells NAnt to resolve all relative paths from that base directory. In this simple case, the basedir is just the current working directory. The default attribute specifies which target to execute if you don’t tell NAnt specifically which task to run. A “target” is analogous to a “method” on a class in .NET. Both are small units of functionality expected to perform a specific task and yield specific results. The preceding snippet does not have any targets, so NAnt will raise an error if you try to execute this build script. So the next thing you need to add is a target named build.

This example of a basic target simply prints a message to the console:

           

When you run build.bat file from the command line, NAnt executes, calling the “build” target. The output should look something like this:

   Target framework: Microsoft .NET Framework 3.5   Target(s) specified: build   build:        [echo] Hello I'm a build!   BUILD SUCCEEDED   Total time: 0 seconds.

You’ve verified that NAnt is wired up properly and the build script has no errors. Now you’ll compile the Visual Studio solution.

Creating the Build Target

With the basic structure set up, you should create some properties (variables) for the more interesting parts of your build. It’s a pain to go back and extract these later, so it usually helps to apply them at the start. I recommend always starting out with the properties defined in the snippet below. Feel free to rename them:

         

You can now reference these properties anywhere a value is allowed using the ${} syntax. You can see an example of this in the value for the sln.file property.

Next, set up the compiler to compile the Visual Studio solution file. As it turns out, Visual Studio 2005 and 2008 project files are actually MSBuild scripts. MSBuild is Microsoft’s build tool (which is similar to NAnt in many ways). Because the VBProj and CSProj files already contain all the information needed to compile your solution, it is actually easier to let MSBuild handle all that for you. NAnt comes in handy for all the other non-compile tasks, such as cleaning up build artifacts, running tests, creating a deployment package, etc. While MSBuild can do these things, it’s not as easy to work with, nor does it have NAnt’s straightforward procedural approach to these types of tasks.

So, I recommend using NAnt for all non-build tasks, and deferring to MSBuild for compilation. This has the added benefit of automatically keeping the build situation consistent between the IDE and the build script so that you don’t get into a situation where the project builds in one but not the other. Also, it preserves all the IDE build behavior (such as post-build behavior, output files, embedded resources, etc.) and it’s one less thing you need to think about in your NAnt script.

To call MSBuild, you must let NAnt know where the msbuild.exe file is. NAnt has some handy functions that allow you to discover where the framework directories are. Don’t hard code your framework directory; let NAnt build it for you. This is particularly useful if you ever need to run the build on another computer, whose framework directory may be on a different drive or in a different directory for some reason. To determine the framework directories, set up some properties that are similar to the ones you’ve already seen, but require some special NAnt syntax:

           

Note the function-call like syntax. NAnt can do more than just store and retrieve properties and variables; it supports a rather complex expression syntax that lets you perform Boolean operations, call NAnt functions, and much more. You’ll see more about that later. For now, you need to call MSBuild.

One more thing first: MSBuild has a large number of command-line options, some of which you always want, so it’s useful to create yet another property that contains these “standard” command-line options. Here’s a property definition for common MSBuild options (feel free to change these as needed):

   

Note that this is a dynamic property (dynamic=true). Setting this property to “dynamic” means that NAnt evaluates the property value just-in-time, as opposed to only once, during startup. If the property weren’t dynamic, then changing the value of project.config would have no effect. Because msBuildArgs would have already been evaluated (at startup), it would always be debug. However, because it’s dynamic, the value will be determined only when it’s requested, so it will factor whatever project.config happens to be at that moment into the value of msBuildArgs. In this way, you can change project.config whenever you like, and the referenced value of msBuildArgs will reflect those changes.

Finally, update the build target to call MSBuild on your Visual Studio solution file:

           

Assuming your Visual Studio solution is compilable, when you re-run build.bat, you should now see something similar to this output:

   Target framework: Microsoft .NET Framework 3.5   Target(s) specified: build   build:        [exec] CopyFilesToOutputDirectory:        [exec]   CoDe.Repeatability.Core ->    trunksourceCoDe.Repeatability.Corein   DebugCoDe.Repeatability.Core.dll   BUILD SUCCEEDED   Total time: 0.6 seconds.

A Few Other Targets to Consider

The msBuildArgs dynamic property you created in the previous section lets you change the value of project.config, giving you the ability to perform a “Release” configuration build with Visual Studio. First, add a new target that changes the value of project.config to release:

           

Then, from the command line, execute:

   build releaseBuild build

This instructs NAnt to run the releaseBuild target first, followed by the build target. Notice that the output has changed slightly; it now compiles to the Release folder:

   Target framework: Microsoft .NET Framework 3.5   Target(s) specified: releaseBuild build   releaseBuild:   build:        [exec] CopyFilesToOutputDirectory:        [exec]   CoDe.Repeatability.Core ->    trunksourceCoDe.Repeatability.Corein   ReleaseCoDe.Repeatability.Core.dll   BUILD SUCCEEDED   Total time: 0.6 seconds.

Your basic build script is now ready to go. Listing 1 shows the entire current build script. If you’ve been following along, and you have a Subversion repository already set up, this would be an ideal time to commit your changes to the repository.

Automated Testing

Takeaway: Project code should have at least a basic success indication check test and it should be able to run in an automated fashion. If you’re not already writing automatic unit tests for your code (using a tool like NUnit, MbUnit, or XUnit.NET), I strongly encourage you to do so.

Author’s Note: Advancement in the art of unit testing over the past few years and evolving patterns and practices have had a huge impact on the way I code and the quality of the software I produce. One particular practice I’ve adopted is known as “Test-Driven Development” (TDD). A more recently refined incarnation is known as “Behavior-Driven Development” (BDD). You may find other patterns such as “Test-First Development” (which is subtly different than TDD), “Test-After Development” and many more. I strongly suggest you investigate these patterns and practices (especially TDD and BDD).

Even if you decide not to pursue the automated unit testing path, you should still have at least one form of automatic test (integration test, smoke test, acceptance test) to confirm that the automated build procedure you put in place from the last section not only successfully compiled the code, but also verifies that the code can run according to expectations. For example, you might consider an ASP.NET Web application verification test successful if the code compiles and your test can execute the “login screen” (or some landing page that triggers the startup procedures of the Web application) without causing something like the ASP.NET “Yellow Screen of Death.” A test that causes a login to occur and bring up the user’s home page, which may involve a database round trip of some kind, would be even better. This would be a good smoke test to know that the application works and that it’s able to connect to and retrieve data from the database.

Griffin Caprio wrote about unit testing in the November/December 2004 issue of CoDe Magazine.

Installing NUnit

To get started with unit testing, I recommend NUnit (though MBUnit and XUnit.NET are very good too, and are definitely worth investigating). The most current stable release version is 2.4.8 at the time of this writing. You’ll find several flavors including an MSI installer and a ZIP file. I usually recommend the ZIP file (NUnit-2.4.8-net-2.0.zip) because I tend to update NUnit as soon as the next version comes out and it’s tedious to install and uninstall MSIs all the time. Also, having the binaries in your “tools” folder makes it easier to maintain version compatibility among different projects.

After downloading the NUnit ZIP file, extract the contents of the bin folder to trunk oolsNUnit. NUnit is now installed and ready to be used.

For more information on NUnit, see Dan Jurgen’s article in the November/December 2004 CoDe Magazine.

Adding the Test Project

In my opinion, it’s generally a good idea to put your unit tests into a separate project, although there’s some debate about that among the developer community. For now, keep them in a separate project?you can always move the tests into your core project(s) later.

Add a new class library project to your Visual Studio Solution and call it something recognizable (such as YourSolution.SomeProject.Tests) so that it appears underneath the YourSolution.SomeProject in Visual Studio’s Solution Explorer. Next, add a reference from your Tests project to the project that will be tested (e.g., YourSolution.SomeProject). Also, you’ll want to add a reference to the nunit.framework.dll file in your trunk oolsNUnit folder.

Adding the First Unit Test

To keep things simple, suppose you want to test a class called SimpleCalculator. Create a new class in your testing project called SimpleCalculatorTester. Add a using directive for the NUnit.Framework namespace and then decorate the class with the [TestFixture] attribute. Add a new public, void method and decorate it with the [Test] attribute. Call the first test should_add_two_numbers_and_get_expected_result(). Your first test class should now look something like this:

   using NUnit.Framework;   namespace CoDe.Repeatability.Core.Tests   {      [TestFixture]   public class SimpleCalculatorTester      {         [Test]   public void should_add_two_numbers_and_get_expected_result()         {            Assert.Fail("TODO: Fill in this test");         }      }   }

You may have noticed the peculiar test name. I recommend that you make tests very descriptive about what they’re doing and what they hope to accomplish. Try to avoid specific numbers or values in the test name?but that’s not a hard and fast rule; sometimes it makes sense. Tests can serve as a form of documentation and help capture real-world requirements about your system (whatever the actual software requirement documentation may say). Descriptive names can help bring new developers up to speed with the assumptions made about how the system should work, and can help prevent all developers from breaking previous assumptions accidentally.

Next, add some actual test code to exercise the tested code and assert your assumptions. Try to keep the tests very small and focused. Ideally, you want to test only one thing per test. Try to avoid “epic” tests that test the database, a Web service, the file system, and seventeen different assumptions all in one test. These tests will be brittle and cause a lot of heartache as the code evolves.

To keep things simple, my SimpleCalculator class has an “Add” method that takes two operands and adds them together. I know, I know, this isn’t very practical but please bear with these simple illustrations.

   [Test]   public void should_add_2_and_2_and_get_4()   {      var calc = new SimpleCalculator();      Assert.That(         calc.Add(2, 2) == 4,         "2 plus 2 should equal 4");   }

Running the Unit Tests

You can execute these unit tests in a couple of ways. NUnit comes with both a GUI/Windows runner as well as a command-line runner. Both are quite capable and useful but I find that having to leave Visual Studio to run my tests adds friction to my development/test cycle. Two utilities make this problem go away: TestDriven.NET and ReSharper’s UnitRunner. Both are commercial software that are well worth the cost. If you’re not determined to run tests in the IDE, the NUnit GUI is free. It can also watch for changes to assemblies, so when you recompile your application it can run your tests automatically, helping you keep up to date with any test failures. The automatic change detection can be just as compelling a feature running the tests directly in the IDE.

Regardless of which runner you choose to use for your development, you should also add the console test runner to your automated build process so that the build is not considered a complete success until all the tests pass.

Adding Unit Testing to the Build

Open the build.xml file again and add a new target called test. This target is dependent upon the “build” target, so you must add the depends=”build” attribute to the element. Inside the target, call the task. The following code shows an example of how the test target should look:

                                                                  

At this point I would also recommend changing the default target for the build script to test rather than build.

Now, run build.bat and your output should look something like this:

   Target framework: Microsoft .NET Framework 3.5   Target(s) specified: test   build:        [exec] CopyFilesToOutputDirectory:        [exec]   CoDe.Repeatability.Core ->    trunksourceCoDe.Repeatability.CoreinDebug   CoDe.Repeatability.Core.dll        [exec] CopyFilesToOutputDirectory:        [exec]   CoDe.Repeatability.Core.Tests ->    trunksourceCoDe.Repeatability.Core.Testsin   DebugCoDe.Repeatability.Core.Tests.dll   test:      [nunit2] Tests run: 1, Failures: 0, Not run: 0,   Time: 0.026 seconds      [nunit2]      [nunit2]      [nunit2]   BUILD SUCCEEDED

At this point, you have a fully automated build and test setup. You can continue adding tests to the Tests project as needed. NUnit will automatically detect and run them as long as you remember to include the [TestFixture] and [Test] attributes on your test classes and methods respectively.

If you ever add another test project, remember that you will need to edit the list of used by the task in the test target in your NAnt build script.

Automated Deployment

Takeaway: Your build product should be deployable to another environment with minimal human interaction. NAnt can really pay off here. Most deployment processes are complicated, conditional, multi-step processes that require a lot of “magic” to come out right. One effective way to address this problem is to keep a master record that explains of how, exactly, the build-and-deploy process works. And the best way to document that process is through the actual code that performs the process. External documents or wikis quickly get out of sync with the real process, and soon become a liability rather than an asset. Using NAnt as your deployment documentation solves two problems at once.

Start by adding a new target to your build.xml file called package (or whatever makes sense to you). It should depend upon the test target to ensure that you don’t package a broken build.

The flow of a release target should go something like this:

  1. Build and test the solution.
  2. Create the release folder if it’s not already there.
  3. Create a temporary folder under the release folder.
  4. Copy the solution output files to the temporary folder (except for PDB files and other files you don’t want in the final release).
  5. Zip up the temp folder’s contents into the main release folder.
  6. Delete the temp folder.

A basic package target will look something like the following:

                                                                                                     

Using the package target, you can copy the .zip file to another computer, extract it, and execute it. If your deployment process involves making an installer of some kind, this would be the place to do it.

Obviously this is an extremely simple package target, and most systems will require many more things. I encourage you to look at the list of pre-canned tasks available in NAnt.

NAnt has a sister project known as “NAnt-contrib” that includes even more pre-built tasks for various purposes. NAnt-contrib is a separate project because it includes specialized tasks that are outside the scope of normal NAnt usage, such as Active Directory interaction, BizTalk utilities, FXCop execution, GAC manipulation, and integration with many different source control providers, including Subversion and Microsoft Visual SourceSafe. Check it out, because you will probably be able to use one or more of these tasks.

Continuous Integration

Takeaway: When a commit takes place against the source control repository, a separate, objective, and impartial system should execute an automated build process and report the build result. By now, you’re probably scratching your head and asking “Why do I need to go through all this hassle again?” Some of it can be pretty trying at first. But after you’ve done it a few times, you’ll find it’s quick to set up. There are tons of example build scripts online that you can grab to help you get started. Testing, depending on how far you go with it, can really pay dividends in the long run, but make no mistake?it is an investment.

You’ll get the maximum payoff from the topics discussed here when you perform them frequently and make them part of your routine. Adhering to the build and testing cycle takes discipline, but soon becomes a habit. It’s like safety belts: At first they can be rather uncomfortable and annoying, but soon you don’t even know they’re there, and eventually, you can’t imagine ever not wearing one. One of the best ways to get yourself and your team to stick to the discipline is to increase the visibility of the tasks, making a computer do them for you and report status.

The easiest way to accomplish this is by using what’s known as “Continuous Integration.” That is, the build gets run every time someone on your team commits code to the source code repository. Any compile errors, test failures, or other problems will be more visible, and helps raise team awareness?and that raised awareness helps remove friction and roadblocks, and eliminates confusion about why a build failed. As time progresses and developers gain trust in the process, successful builds can be made available as deployment packages for testers or managers to review to keep track of the team’s progress.

There are two popular software packages in the .NET space that provide continuous integration services: ThoughtWorks’ CruiseControl.NET, which is free/open source and JetBrains’ TeamCity which is a commercial product (i.e., not open sourced), but is free for small or medium teams (see the licensing terms for more information). TeamCity offers some compelling features over CruiseControl.NET, including simpler setup, a more user-friendly interface, and the ability to run a build on three different computers simultaneously. JetBrains calls the build computers “agents.” The neat thing about agents is that they can be running on different platforms with different configurations. For example, you could run your build and tests against SQL Server 2005 on Windows 2000, Oracle 9i on Windows Server 2008, and MySQL on Windows Server 2003. There is a limit to the number of projects you can host in TeamCity and the number of build agents you can use before you have to pay for the software. But for most purposes, the free version of TeamCity provides plenty of room plus some for expansion. I recommend you use TeamCity to get up and running. If you outgrow the free version of TeamCity you can decide whether you want to pay for the enterprise version or move to CruiseControl.NET. At that point, you’ll have more experience with Continuous Integration, and the decision will be easier.

Before getting into the “How” you may want to know more about the “Why,” so I recommend you read Jeremy Miller’s article on Continuous Integration.

Preparing a Integration Box

You can use just about any Windows XP or later computer to run the integration and build; Windows Server 2003 and 2008 work fine also. Depending on which version of Windows you have and what type of project you’re building (.NET 2.0/VS2005 or .NET 3.5/VS2008), you’ll want to make sure that you have .NET 2.0 and the 2.0 SDK installed. If you’re using .NET 3.5/VS2008, make sure that the .NET 3.5 Framework as well as the Windows SDK 6.1 (which includes the .NET 3.5 Framework SDK) are installed.

Author’s Note: I strongly advise you not to install any version of Visual Studio on your integration box. It may make setup a little easier, but doing so eliminates most of the impartiality that the integration box brings to your environment.

Also, NAnt 0.86 currently has an issue when running on Windows Server 2008 that you can easily fix yourself. To do that, download the latest NAnt build (0.86 nightly or later) and use that in your toolsNAnt folder. Open the NAnt.exe.config file in a text editor and find where it references the “v6.0A” version of the Windows SDK. Change that to “v6.1” to make it work properly with the Windows Server 2008 and .NET Framework 3.5 SDK (Windows SDK v6.1). After you take these steps, your NAnt build should run fine on Windows Server 2008.

Setting up TeamCity

?
Figure 7. TeamCity Installer Options: If this is your first time installing TeamCity, accept all the defaults.

At the time of this writing, TeamCity 3.1.1 is the latest available version, which you can download here. Make sure you get the Professional (free) edition. Run the installer (see Figure 7), and (at least your first time) accept all the defaults. Later, you can specify build agent and work folder paths on different disks as your needs demand. At first, keep things simple.

When the TeamCity installer completes, you’ll see a configuration screen (see Figure 8) that lets you change the basic configuration settings. The default settings are usually fine and you can safely click Close. You’ll see a Finished screen that provides the option to open the web management site for TeamCity. Click Finish and wait for the web management site to load for the first time. Finally, you’ll need to create the administrator account for TeamCity’s web management application. When that’s done, TeamCity is officially installed and you can begin adding new projects.

?
Figure 8. TeamCity Advanced Options: You set basic configuration settings on this screen, but the default settings are usually fine.

Configuring a Project in TeamCity

Click the “Create Project” link to get started. Give your project a name and a brief description and click the Create button.

You’ll need to create a “Build Configuration.” A configuration defines your version numbering scheme, the products of your build (TeamCity calls them “artifacts”), and how to interact with your source control system for this build. For now, add a new build configuration with a name. Leave the rest alone and click the VCS Settings button.

Attaching the Project to Subversion

?
Figure 9. Connecting to Subversion: The settings shown here illustrate configuring TeamCity’s connection to a Subversion server.

Next, you need to tell TeamCity about your Subversion source control repository, so it can begin listening for changes and take appropriate action. Click the “Create and attach new VCS root” link to enter the details of your Subversion repository (see Figure 9). Make sure that when you end your Subversion repository URL with /trunk to prevent TeamCity from grabbing all the branches and tags every time you run a build (see the sidebar “Trunks, Branches, and Tags“). At this point I suggest that you fire up your Visual SVN Server admin console program and add a new user for TeamCity to use when connecting to SVN. While not required, that can help later with logging and access to the system.

This configuration screen shown in Figure 9 has many options. For now, I recommend leaving all alone. You can configure them later if necessary. However, make sure you click the Test Connection button and, finally, click Save when you’re done.

Clicking Save from the screen shown in Figure 9 takes you back to the “Build Configuration” screen, at which point you should click the “Choose Build Runner” button to begin telling TeamCity what to do with your source code, now that it has access to your repository.

Configuring the Build Runner

On this screen, you tell TeamCity how to run your build. TeamCity supports many different types of build runners including NAnt. Choose NAnt as the runner. In the “targets” textbox, type “package.” Next, change the NAnt Home option to point to your tools folder using a special variable that TeamCity has to identify where the current code that’s being built and tested actually resides. This is a transient working directory, so you can’t simply hard-code it. Placing the value %system.teamcity.build.checkoutDir% oolsNAnt for the NAnt Home option will keep things properly relative. This ensures that TeamCity is using the version of NAnt that you want for your specific project. If you ever change your version of NAnt, you won’t have to update the build server. Finally, click Save to complete this screen.

At this point, you have a basic CI environment complete with source control, automated build, test, and deployment and continuous integration among the developers.

Set Build Triggering

?
Figure 10. TeamCity Project Status: When TeamCity completes a build, it lets you know how many tests passed.

One last setup step you should consider is setting up the build so it launches automatically when someone commits files to the Subversion repository. This is normally how CI works, but in TeamCity this option is off by default. On the right-hand side of the screen, when editing the Build Configuration, step number 4 is “Build Triggering.” Click this step and then check the checkbox to enable triggering.

You may also want to consider a “quiet period” of 60 seconds or so, which helps handle the case when many people commit code at nearly the same time, which would otherwise trigger many builds.

Testing the Build
To test that the build is working, pick a file and save it (add an empty whitespace somewhere, etc.). Commit the file to Subversion. Open the TeamCity web application and click on the Projects tab. After a minute or two, you should notice that your project has an active build running. With everything configured correctly, the build will complete successfully (see Figure 10) and let you know how many tests passed.

Repeatability is all about reducing friction, surprises, and lost work due to otherwise preventable problems. While there is some startup cost and pain involved when introducing repeatability into your development process, the return on investment is fast and leads to a sustainable pace of success well into the future.

Finally, you don’t need to apply every recommendation here at once. You can add them incrementally. I encourage you to give some of them an honest try and see how well they benefit your environment and affect your bottom line.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist