Unit testing has been around for quite a while and a lot of developers have created their own unit tests. Even being a simple concept and having been around for some time I still think that unit tests are misunderstood, poorly written and don't follow best practices by a majority of developers. I'm not a unit testing expert but there are some guidelines that I try to follow when I create unit tests some of those are listed below (in no particular order).

  • Make them small
  • Make them self contained
  • Make the names describe what the test case tests (at the price of a long name)
  • Follow the arrange/act/assert pattern, i.e. don't do something like arrange/act/assert/act/assert, etc.
  • Don't make database calls!
  • Use mocks when necessary to ensure that you test the correct piece of code
  • Consider making methods/properties to be internal instead of private. You can use the InternalsVisibleTo attribute to allow testing of internal items.
  • Don't have app.config for your unit tests. Unit test should "arrange" the configuration in code.

If you make your unit tests too long and to test more than one item then they will become a pain to maintain, which leads to them being ignored and eventually to a complete breakdown to your testing effort in general.

I have recently been thinking more about how unit tests are being used throughout software development. Also I've been reading ASP.NET MVC in Action which coincidently puts a lot of emphasis on unit testing. I found a few quotes in Chapter 3 of that book, which covers Controllers, and thought that I would share them here because I agree with statements whole heartedly. Text in italics are my own comments.

  • Unit tests run fast because they do not call out of process (i.e. no database calls, no web service calls, etc)
  • It is very difficult to test poorly designed code. (Do you ever have a set of code and think to yourself, "I can't write unit tests for this, it's impossible", at this point you should consider to refactor it because this is a red flag for bad code!)
  • (While discussing mocking)… unit testing becomes easy and soon becomes a repetitive pattern of faking dependencies and writing assertions. Over time, if you employ this technique, you will see a marked improvement in the quality of your code.
  • A good controller unit test runs fast. We are talking 2000 unit tests all running within 10 seconds.
  • It's nearly impossible to test-drive code that ends up with a bad design. (Once again bad code is hard to test)
  • Pay attention to pain. If your tests become painful to maintain, there's something wrong.
  • Correctly managed design and tests enable sustained speed of development whereas poor testing techniques cause development to slow down to a point where testing is abandoned.

Some of the key take aways: use mocks, and testing should be helpful to your development process not a hindrance.

Don't write unit tests just to write them or just to get the necessary coverage. Make sure that you write the test cases with a specific intent and that they are written well so that you will continue to maintain and execute them as a part of your CI build process.

Here are some unit tests that I've recently written that I'd like to use to show some concrete cases.

[Test]

public void TestGetConfigValue_ValueExistsRequiredTrue()

{

    string key = "83F216CD-1B3E-4fc1-9DA5-4A7D506AF7E8";

    string expectedValue = "C2D4E7CA-BE6B-4976-BFA9-50F5223C603A";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueExistsRequiredFalse()

{

    string key = "0601AB1F-C78C-4c72-8648-B140D50BDDEC";

    string expectedValue = "92DCADBC-9618-49f3-A412-5C90A945903D";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredFalse()

{

    string key = "2D4B5C66-BA37-4ab8-9B51-3F17CF348E6D";

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.IsNull(actualValue);

}

 

[ExpectedException(typeof(ConfigurationErrorsException))]

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredTrue()

{

    string key = "0511216E-3AA6-4b80-B215-E980A459466D";

 

    // ConfigurationErrorsException here

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredFalse()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, false);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredTrue()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, true);

}

 

// Setup method here

[SetUp]

public void Reset()

{

    ServiceConfigHelper.Instance.Reset();

    if (ConfigurationManager.AppSettings.HasKeys())

    {

        List allKeys = new List();

        foreach (string key in ConfigurationManager.AppSettings.Keys)

        {

            allKeys.Add(key);

        }

 

        foreach (string key in allKeys)

        {

            ConfigurationManager.AppSettings[key] = string.Empty;

        }

    }

}

Notice that each of these has a very descriptive name, if you saw the name of the test in the list of failed test cases you immediately know what to work on; you don't even have to look at the test. Notice that all of the tests are pretty small, and that each test case tests a very specific piece of functionality. Also you might have noticed that I use Guids in test cases I like to do this just to make sure that I have random data and that test cases don't conflict with each other.

Also I'd like to note that in the book the guys recommend reading Working Effectively with Legacy Code for good coverage of unit testing, I haven't read it but I might grab a copy.

 

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


A while back someone asked me if you could sync 2 or more folders with one statement using MSDeploy. I said of course, if you perform the sync using manifest files. Manifest files allow you to "group" sync operations into a file. When you invoke msdeploy.exe and point it to a manifest file, each provider will be executed in the order in which it appears inside the manifest file. A common scenario for using manifest files is to sync websites. This way you can specify the files that should be synced, the website (application) name, ACL values, etc. But you are not limited to using manifest files for web related sync operations. When using manifest files, you would specify the provider to be manifest. We will see this in the command used to snyc two folders. Often times when using a manifest file for the source you will also use one for the destination. Here are the two files.

SourceManifest.xml

 

 

DestManifest.xml

 

 

In this example I am syncing two folders C:\temp\MSDeploy\Source01 and C:\temp\MSDeploy\Source02 to another drive location on E. The command to perform the sync would be

msdeploy -verb:sync -source:manifest=sourceManifest.xml -dest:manifest=destManifest.xml

And here are the results of that sync operation, when the destination directories don't exist.

C:\temp\MSDeploy>msdeploy -verb:sync -source:manifest=sourceManifest.xml -dest:manifest=destManifest.xml

Info: Adding sitemanifest (sitemanifest).

Info: Adding contentPath (E:\temp\MSDeploy\Source01).

Info: Adding dirPath (E:\temp\MSDeploy\Source01).

Info: Adding child filePath (E:\temp\MSDeploy\Source01\01.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source01\02.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source01\03.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source01\04.txt).

Info: Adding contentPath (E:\temp\MSDeploy\Source02).

Info: Adding dirPath (E:\temp\MSDeploy\Source02).

Info: Adding child filePath (E:\temp\MSDeploy\Source02\01.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source02\02.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source02\03.txt).

Info: Adding child filePath (E:\temp\MSDeploy\Source02\04.txt).

Total changes: 13 (13 added, 0 deleted, 0 updated, 0 parameters changed, 0 bytes copied)

As you can see the destination directories were created and the files synced into the destination folders. First all the content of the Source01 folder is synced and then the Source02 folder as expected. If you perform the sync operation and all files are up-to-date then no changes will be made.

This is just a very basic example of how you can use MSDeploy manifest files to perform a sync operation, but you can create manifest files that perform many different actions. Visual Studio 2010 uses manifest files when it creates the web packages for deployment.

 

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


MSBuild Video

Comments [0]

I have a new video posted discussing MSBuild, you can see this video at http://www.dnrtv.com/default.aspx?showNum=158. This is a continuation of my first appearance discussing on dnrtv.com. Take a look and let me know if there are any particular topics you'd like for me to discuss. One of the topics I discuss in this video is Open Source MSBuild tasks. One of the most useful task libraries is the MSBuild Extension Pack, there is a great set of tasks there. If you haven't seen it already you should take a look.

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


MSBuild Reserved Characters

Comments [1]

The other day someone sent me an email asking me about reserved characters in MSBuild targets names. I didn't actually know of a published list of such a reference so I contacted the MSBuild team for more information and here is what I found out.

Illegal characters in target names

'$', '@', '(', ')', '%', '*', '?', '.'

Properties, items and item metadata elements must start with either a letter [a-Z] or an underscore (_).

Characters after the initial character for properties, items and item metadata can include: letters, numbers, underscore, and dashes (-).

 

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


ASP.NET MVC Sudoku Solver

Comments [0]

A while back I had a session on MSDeploy where I demonstrated deploying an ASP.NET MVC Sudoku solver that I wrote a while back. A few people asked me to publish it, so I did you can get it at http://sudokumvc.codeplex.com/

 

Sayed Ibrahim Hashimi


Comment Section

Comments are closed.


<< Older Posts | Newer Posts >>