Try-Catch-FAIL

Failure is inevitable.

A troublesome brew: NHibernate + FluentNHibernate + SQLite on .NET 4.0

clock March 9, 2010 15:20 by author Matt

Mixing NHibernate, Fluent NHibernate, and SQLite on .NET 4.0 RC will lead to some major headaches.  There’s an easy fix, but it doesn’t work if you are using Resharper’s test runner.  If you’re using my personal favorite test runner, TestDriven.NET though, the fix is actually quite easy. 

We decided to use .NET 4.0 for the new super secret social networking site we’re working on, and since we’re starting out with a relational DB under the hood (that we fully plan to swap out for a NoSQL alternative later), we’re using NHibernate.  I hate NHibernate’s XML maps, so we’re also using FluentNHibernate.  When I’m using NHibernate, I like to use SQLite’s in-memory database for my unit tests, so now we’re looking at making three pieces of OSS play nice together on a not-quite-finished version of the .NET Framework.  Oh, and I’m also using Resharper 5.0 EAP.  :)

Getting things started is easy enough.  I grabbed the latest production release of NHibernate along with a compatible version of Fluent NHibernate.  I also grabbed the latest SQLite .NET assembly.  After adding references to the required bits, here’s my NHibernate bootstrapper:

public static class NHibernateBootstrapper
{
    private static Configuration _currentConfig;

    private static FluentConfiguration ConfigureMappings()
    {
        return Fluently.Configure()
            .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
            .ExposeConfiguration(c => _currentConfig = c);
    }

    public static ISessionFactory CreateSessionFactoryForTests()
    {
        return ConfigureMappings()
            .Database(SQLiteConfiguration.Standard.ShowSql().InMemory())
            .BuildSessionFactory();
    }

    public static ISessionFactory CreateSessionFactoryForProduction()
    {
        return ConfigureMappings()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("RageFeedData")))
            .BuildSessionFactory();
    }

    public static void CreateSchema(ISession session)
    {
        var exporter = new SchemaExport(_currentConfig);
        exporter.Execute(true, true, false, session.Connection, null);
    }

    public static void UpdateSchema()
    {
        var updater = new SchemaUpdate(_currentConfig);

        updater.Execute(true, true);
    }
}

And here’s my unit tests:

public abstract class InMemoryDatabaseFixture
{
    public ISession Session { get; private set; }

    [SetUp]
    public virtual void Prepare()
    {
        var factory = NHibernateBootstrapper.CreateSessionFactoryForTests();
        Session = factory.OpenSession();
        NHibernateBootstrapper.CreateSchema(Session);
    }

    [TearDown]
    public virtual void Cleanup()
    {
        Session.Dispose();
    }
}

[TestFixture]
public class RageletPersistanceTests : InMemoryDatabaseFixture
{
    [Test]
    public void CanCorrectlyPersistRagelet()
    {
        new PersistenceSpecification<Ragelet>(Session)
            .CheckProperty(r => r.Content, "Test content")
            .VerifyTheMappings();
    }
}

Simple enough, right? 

boom

Sadly, this fails gloriously when run with ReSharper’s test runner:

Mixed mode assembly is built against version 'v2.0.50727' of the runtime
and cannot be loaded in the 4.0 runtime without additional configuration information.

I found a solution, but it doesn’t work with Resharper 5.0 EAP.  I’m thinking that Resharper isn’t using the app.config file, but perhaps it’s something else.  Fortunately, running the tests with TestDriven.NET (my favorite test runner) worked perfectly.

Unfortunately, I wasted the better part of two weeks of “free time” trying to get things recompiled so that they would work without the app.config entry.  There are several breaking changes in .NET 4.0 apparently, and nothing that I tried worked.  Hopefully this isn’t a sign of pains to come. 

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Using web.config files in ASP.NET MVC unit tests

clock August 3, 2009 02:44 by author Matt

TestDriven.NET (and apparently the Resharper test runner) will automatically pick up app.config files, making the contents available to unit tests, but they do not pick up web.config files in web projects.  After some Googling, I found this post that describes the solution.  To paraphrase, just add a post-build event that copies the web.config file to the bin folder and gives it the correct name, like so:

copy /Y “$(ProjectDir)Web.config” “$(TargetDir)$(TargetFileName).config

Thanks, Sebastien!

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Unit Testing in .NET Part 5 - Moq-ing your way to simpler tests

clock January 22, 2009 07:30 by author Matt

Previously on "Unit Testing in .NET", we've looked at:

That means you should be comfortable with the all the fundamental concepts of software testing, and you should be equipped to deal with some common testing headaches.  In this post, we're going to look at how to simplify testing via dependency injection and the excellent mocking library, Moq.

What is mocking?

Mocking refers to the act of substituting a simulated object in place of a real object.  In unit testing, it is quite common to use mocking to separate the class-under-test from its external dependencies, making the class much easier to fully test.  I didn't talk much about it, but that's exactly what we were doing in the previous post when we applied dependency injection and the adapter pattern.  We were substituting a fake object (the mock) that we had given a specific behavior to for the real object. 

Sidetrack: what's the difference between a mock object, a fake object, and a stub?
Nothing that really matters, in my opinion.  Purists will argue the semantic differences between them, but I don't think that's really helpful as long as you use a mocking framework that is flexible enough to meet your testing needs.

Moq

Moq (I prefer the "mock" pronunciation just because it's only one syllable instead of two) is a newer mocking framework for .NET that leverages many of the new platform and language features that arrived in .NET 3.5.  This gives it an advantage over older mocking frameworks that were stuck using the features that were available in previous versions of .NET. 

Getting started with Moq is easy.  Just grab the DLL, add a reference to it, and start coding.  Note that we will be using Moq 3.0 Beta, and that there are some major changes from 2.6 to 3.0, so the code below may need to be modified somewhat if you are using an older version.

Our First Test

Let's revisit our LogMonitor from the previous post.  Recall that this class takes in a log message, then dispatches an E-mail if the message contains the string "error".  Originally, the class was tied directly to SmtpClient, but we applied the adapter pattern and dependency injection to simplify the tests.  Here's the original LogMonitor code:

   1: /// <summary>
   2: /// Inspects log messages and sends E-mails if an error occurs.
   3: /// </summary>
   4: public class LogMonitor
   5: {
   6:     /// <summary>
   7:     /// The client used to send E-mail
   8:     /// </summary>
   9:     private ISmtpClient mClient;
  10:  
  11:     /// <summary>
  12:     /// Creates a monitor that will use the default SMTP client.
  13:     /// </summary>
  14:     public LogMonitor() : this(new SmtpClientAdapter())
  15:     {
  16:         
  17:     }
  18:  
  19:     /// <summary>
  20:     /// Creates a monitor that will use the specified 
  21:     /// <see cref="ISmtpClient"/>.
  22:     /// </summary>
  23:     /// <param name="smtpClient"></param>
  24:     public LogMonitor(ISmtpClient smtpClient)
  25:     {
  26:         mClient = smtpClient;
  27:     }
  28:  
  29:     /// <summary>
  30:     /// Inspects log messages, and sends an E-mail if a message
  31:     /// contains the word "error".
  32:     /// </summary>
  33:     /// <param name="message"></param>
  34:     public void InspectLogMessage(string message)
  35:     {
  36:         if (message.ToUpper().Contains("ERROR"))
  37:         {
  38:             mClient.Send("system@somewhere.com", "support@nospam.com", "Error!", message);
  39:         }
  40:     }
  41: }
  42:  
  43: /// <summary>
  44: /// Wrapper interface for <see cref="SmtpClient"/>.
  45: /// </summary>
  46: public interface ISmtpClient
  47: {
  48:     /// <summary>
  49:     /// Sends an E-mail.
  50:     /// </summary>
  51:     /// <param name="from"></param>
  52:     /// <param name="recipients"></param>
  53:     /// <param name="subject"></param>
  54:     /// <param name="body"></param>
  55:     void Send(string from, string recipients, string subject, string body);
  56: }
  57:  
  58: /// <summary>
  59: /// Adapts <see cref="SmtpClient"/> into the <see cref="ISmtpClient"/>
  60: /// interface.
  61: /// </summary>
  62: public class SmtpClientAdapter : ISmtpClient
  63: {
  64:     /// <summary>
  65:     /// Sends an E-mail.
  66:     /// </summary>
  67:     /// <param name="from"></param>
  68:     /// <param name="recipients"></param>
  69:     /// <param name="subject"></param>
  70:     /// <param name="body"></param>
  71:     public void Send(string from, string recipients, string subject, string body)
  72:     {
  73:         SmtpClient client = new SmtpClient();
  74:         client.Send(from, recipients, subject, body);
  75:     }
  76: }

And the LogMonitor tests:

   1: /// <summary>
   2: /// Tests fixture for <see cref="LogMonitor"/>.
   3: /// </summary>
   4: [TestFixture]
   5: public class LogMonitorTests
   6: {
   7:     /// <summary>
   8:     /// Test class.
   9:     /// </summary>
  10:     private class TestSmtpClient : ISmtpClient
  11:     {
  12:         /// <summary>
  13:         /// Incremented every time a message is sent.
  14:         /// </summary>
  15:         public int MessagesSent { get; private set; }
  16:  
  17:         /// <summary>
  18:         /// Records that a message was sent.
  19:         /// </summary>
  20:         /// <param name="from"></param>
  21:         /// <param name="recipients"></param>
  22:         /// <param name="subject"></param>
  23:         /// <param name="body"></param>
  24:         public void Send(string from, string recipients, string subject, string body)
  25:         {
  26:             Console.WriteLine("Received message: {0}", body);
  27:             MessagesSent++;
  28:         }
  29:     }
  30:  
  31:     /// <summary>
  32:     /// Verifies that the method correctly sends an E-mail 
  33:     /// when it receives an error log.
  34:     /// </summary>
  35:     [Test]
  36:     public void InspectLogMessage_SendsErrorEmail()
  37:     {
  38:         TestSmtpClient client = new TestSmtpClient();
  39:  
  40:         LogMonitor monitor = new LogMonitor(client);
  41:  
  42:         monitor.InspectLogMessage("This is an error.");
  43:  
  44:         Assert.AreEqual(1, client.MessagesSent);
  45:     }
  46: }

Notice that we had to create a test class to serve as the mock object for LogMonitor.  All is well.  But wait, the requirements are about to change!  Now we need to extend LogMonitor to handle warning messages, and warning and error messages need to go to two different accounts.  Well, we *could* modify our test class to track the E-mails that were sent, then verify that the correct E-mail was sent for each log message we pass in, but then what's going to happen when they want to add support for "fatal" log messages?  Or super-critical-urgent messages?  Our simple test class is going to become complicated in a hurry. 

Enter Moq.  Moq let's us get rid of the test class for ISmtpClient completely.  Instead, we'll let Moq dynamically create a mock object for us.  All we have to do is setup the expectations, the actions we expect the mock object to perform in response to certain method calls.  In Moq 2.x, you did this using the Expects method, but in Moq 3.0, you use the new Setup method.  For our first test, let's verify that error messages are being sent to the new address by using Moq:

   1: /// <summary>
   2: /// Verifies that the method correctly sends an E-mail 
   3: /// when it receives an error log.
   4: /// </summary>
   5: [Test]
   6: public void InspectLogMessage_SendsErrorEmail()
   7: {
   8:     var mock = new Mock<ISmtpClient>(MockBehavior.Strict);
   9:  
  10:     mock.Setup(c => c.Send("errors@somewhere.com", "support@nospam.com", "Error!", It.IsAny<string>())).AtMostOnce();
  11:  
  12:     LogMonitor monitor = new LogMonitor(mock.Object);
  13:  
  14:     monitor.InspectLogMessage("This is an error.");
  15:  
  16:     mock.Verify(c => c.Send("errors@somewhere.com", "support@nospam.com", "Error!", It.IsAny<string>()));
  17: }

There are a few important things to call out here.  First, we are creating a strict mock, which means that any calls to the mock which aren't configured using the Setup method will result in exceptions.  Note how we setup our expectations by specifying the exact values we expect for most of the parameters, but we use It.IsAny for the last parameter.  That says "match any string value" for the last argument.

Second, the mock reference we create can't be passed directly in as an ISmtpClient.  You might think that Moq could provide an automatic cast or something, but the .NET framework just won't allow it in the current version.  So, to get to the actual mocked object, you access the Object property.  Simply pass mock.Object in to LogMonitor just as you would any class that implements ISmtpClient

Finally, notice that we have a call to Verify at the end.  This will throw an exception if the exact call specified wasn't executed.  It is important to make sure that this matches the call you specified to Setup, otherwise your test will fail. 

Go ahead and run the test.  It should fail because we haven't changed the address that error E-mails are being sent to.  Do that now by editing LogMonitor, replacing "system@somewhere.com" with "errors@somewhere.com".  Re-run the test, and it should pass.

Next, let's add a test for the warning messages.  It looks almost identical to the test for errors:

   1: /// <summary>
   2: /// Verifies that the method correctly sends an E-mail when
   3: /// it receives a warning log.
   4: /// </summary>
   5: [Test]
   6: public void InspectLogMessage_SendsWarnEmail()
   7: {
   8:     var mock = new Mock<ISmtpClient>(MockBehavior.Strict);
   9:  
  10:     mock.Setup(c => c.Send("warnings@somewhere.com", "support@nospam.com", "Warning!", It.IsAny<string>())).AtMostOnce();
  11:  
  12:     LogMonitor monitor = new LogMonitor(mock.Object);
  13:  
  14:     monitor.InspectLogMessage("This is a warning!");
  15:  
  16:     mock.Verify(c => c.Send("warnings@somewhere.com", "support@nospam.com", "Warning!", It.IsAny<string>()));
  17: }

Again, run the test to be sure that it fails before you start making changes to the class-under-test.  Once you're sure it fails, change the InspectLogMessage like so:

   1: /// <summary>
   2: /// Inspects log messages, and sends an E-mail if a message
   3: /// contains the word "error".
   4: /// </summary>
   5: /// <param name="message"></param>
   6: public void InspectLogMessage(string message)
   7: {
   8:     if (message.ToUpper().Contains("ERROR"))
   9:     {
  10:         mClient.Send("errors@somewhere.com", "support@nospam.com", "Error!", message);
  11:     }
  12:     else if (message.ToUpper().Contains("WARN"))
  13:     {
  14:         mClient.Send("warnings@somewhere.com", "support@nospam.com", "Warning!", message);
  15:     }
  16: }

Run the test, and everything should pass!

More Mocking

We've only looked at a *very* trivial example so far, so you may be asking questions like "Can I mock a method that returns something?" and "Can I mock events and properties?"  The answer to both these questions is "yes!"  The Moq website has a very extensive Quick Start that illustrates the full power of Moq (note that it hasn't been updated for Moq 3.0 yet, so translate Expect to Setup as you are reading the code).  Let's look at a few of the really cool features.

Automatically mock properties

You can easily stub-out simple properties using the Stub extension method, like so:

   1: [Test]
   2: public void Stub_DoesGreatThings()
   3: {
   4:     var context = new Mock<HttpContextBase>();
   5:     //We want context to track the User property.
   6:     context.Stub(c => c.User);
   7:  
   8:     //When we assign the value here, it is automatically persisted for us, no
   9:     //need to wire it in manually.
  10:     context.Object.User = new GenericPrincipal(new GenericIdentity("Matt"), new[] {"Blogger"});
  11:  
  12:     Assert.AreEqual("Matt", context.Object.User.Identity.Name);
  13: }

Try that example both with and without the call to Stub to see the difference.  For more complicated properties, or to assign a default, use the overload that takes a second parameter:

   1: [Test]
   2: public void Stub_WorksWithComplexTypes()
   3: {
   4:     var context = new Mock<HttpContextBase>();
   5:     //This attaches a new handler to the Handler property.
   6:     context.Stub(c => c.Handler, new DefaultHttpHandler());
   7:  
   8:     Assert.IsNotNull(context.Object.Handler);
   9: }

You can also stub out all properties on the mock in a single call using StubAll.

Recursive Mocking

Need to mock an entire object graph?  Moq has you covered:

   1: [Test]
   2: public void RecursiveMock()
   3: {
   4:     var context = new Mock<HttpContextBase> {DefaultValue = DefaultValue.Mock};
   5:  
   6:     //These properties are now mocked!
   7:     Assert.IsNotNull(context.Object.Request);
   8:     Assert.IsNotNull(context.Object.Response);
   9:  
  10:     //And we can get back the mocked reference and setup expectations!
  11:     var response = Mock.Get(context.Object.Response);
  12:     response.Setup(r => r.Write("Hello, world!"));
  13:  
  14:     context.Object.Response.Write("Hello, world!");
  15:  
  16:     response.Verify(r => r.Write("Hello, world!"));
  17: }

Every return value that can be mocked (meaning it is an interface or an unsealed class with overridable members) will be mocked.  You can get the Mock wrapper around a mocked member via the Mock.Get static method.  This is *very* handy when you are mocking complicated types (you know, like HttpContextBase in your ASP.NET MVC applications).

The Dangers of Mocking

Moq makes it very easy to create mocks, but you should be careful.  Mocking is *not* a substitute for good design, and be careful not to write tests that depend on the internal behavior of the method you are testing.  Remember that your tests should verify that a method does what it says it will do; it's not meant to verify how the method is doing it.  Here's a great example of a bad test:

   1: [Test]
   2: public void BadTest()
   3: {
   4:     var mock = new Mock<IDatabase>();
   5:     mock.Setup(d => d.GetCustomer(5));
   6:     mock.Setup(d => d.SaveCustomer(It.IsAny<Customer>()));
   7:  
   8:     CustomerController controller = new CustomerController(mock.Object);
   9:  
  10:     //All this should do is update the record in the database.  For now, 
  11:     //let's assume it does this by retrieving the old record, updating it,
  12:     //then pushing everything back into the DB.
  13:     controller.ChangeCustomerAddress(5, "1111 New Street");
  14:  
  15:     //This isn't really part of the ChangeCustomerAddress contract, is it?
  16:     //Why do we care how it gets the customer...
  17:     mock.Verify(d => d.GetCustomer(5));
  18:     //But this one, we do care about; we do care that the Customer was pushed
  19:     //back to the database.  In fact, we might want to tighten down this verify...
  20:     mock.Verify(d => d.SaveCustomer(It.IsAny<Customer>()));
  21: }

Notice how we're verifying that the method is making a call to our mocked database.  Well, what happens if we decide to implement some sort of caching scheme in our class-under-test?  The method is still doing exactly what it says its going to do, but it will no longer make a call to our mocked database.  If we added caching, we'd have to change our test even though the actual behavior of our method hasn't changed (which is Bad). 

Wrapping It Up

Well, I hope that was a useful crash-course on using Moq.  For the next post in this series, I'm planning on screen-capturing a brief coding session of me doing test driven development.  The only snag is that I've got to find a project that I can show (obviously my current employer isn't too keen on sharing code that they're paying for).  Because of that, it may be a little while before you see part 6, but hopefully I'll have some other topics to talk about Real Soon Now.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Unit Testing in .NET Part 4 - Overcoming Testing Hurdles

clock January 13, 2009 15:22 by author Matt

By this point in the series, you should know quite a bit about the basics of unit testing in .NET.  You should be able to write unit tests using NUnit, and you (hopefully) understand the value that unit testing brings to the table.  As you've started to apply what you've learned though (and you are applying what you've learned, riiiiight?), you might have run into things that were difficult to create test cases for.  Worse yet, maybe you ran into things that you couldn't test at all.  Fret not, as post 4 in this series is here to save the day.  In this post, we'll look at some of the basic testing problems that you are likely to encounter and how to overcome them.  Let's get started:

Classes with complicated setup

First, some classes require complicated setup or configuration before they're testable.  If you have multiple test cases for your class, you could end up with that setup code repeated in a lot of places.  Having to rewrite all that code is painful, and it will probably make you cut corners to keep your number of test cases low.  Even if it doesn't, what happens if the setup or configuration steps for the class change?  Now you have to change that code in a lot of places. 

The solution is simple: Don't Repeat Yourself (aka the DRY principle).  DON'T repeat the configuration or setup code.  Encapsulate it instead.  Some people choose to make a field in the test fixture for the class-under-test (the class we're creating unit tests for), then use a test case setup method to populate the instance for each test case like so:

   1: [TestFixture]
   2: public class ComplicatedClassTests
   3: {
   4:     private ComplicatedClass mInstance;
   5:  
   6:     [SetUp]
   7:     public void TestSetup()
   8:     {
   9:         mInstance = new ComplicatedClass("http://localhost/testApp", new Random(), new OtherDependency(), "name", 12345);
  10:         mInstance.EnableNotifcations();
  11:         mInstance.SomeOtherSetup();
  12:     }
  13:  
  14:     [Test]
  15:     public void Operation_ReturnsTrue()
  16:     {
  17:         Assert.IsTrue(mInstance.Operation());
  18:     }
  19: }

As a matter of personal preference, I tend to avoid this approach.  What happens when you have a test case that needs to configure the class-under-test a little differently than usual?  You could just reset ignore the private field and make a local instance, but that private field has still been instantiated.  Is it hurting anything?  No, probably not, but it's still there, and that bugs me.  Plus, I don't like test fixtures having state, which is exactly what a private field is.

Instead, I encapsulate the common setup logic in a private helper method that my tests can call to grab a fresh reference to the class-under-test, like so:

   1: [TestFixture]
   2: public class ComplicatedClassTests
   3: {
   4:     private ComplicatedClass GetTestInstance()
   5:     {
   6:         ComplicatedClass instance = new ComplicatedClass("http://localhost/testApp", new Random(), new OtherDependency(), "name", 12345);
   7:         instance.EnableNotifcations();
   8:         instance.SomeOtherSetup();
   9:  
  10:         return instance;
  11:     }
  12:  
  13:     [Test]
  14:     public void Operation_ReturnsTrue()
  15:     {
  16:         ComplicatedClass instance = GetTestInstance();
  17:         Assert.IsTrue(instance.Operation());
  18:     }
  19: }

If the test case just needs an instance with the default configuration, it can call the helper method, otherwise, it can perform it's own setup and configuration.  Yes, you are technically repeating yourself since many of the test cases will have a line of code to retrieve the class-under-test, but DRY is more of a guideline than an actual rule. :P

Addendum: One time when I will go the test setup route is when the class-under-test also requires some special cleanup.  While this should be avoided if at all possible, sometimes it's necessary.  In those cases, I will use a test setup method combined with a test tear-down method.

Tight Coupling

In a perfect world, all your classes would be fully self-contained and would have no dependencies on other classes, making it very easy to create unit tests that only stress the class-under-test.  In the real world though, this almost never happens.  Depending on what the class-under-test is dependent on, this can be a real problem.  Suppose your class makes calls into some other class, and suppose those calls are expensive (on the order of minutes).   Suppose that you already have thorough unit tests for that other class.  Well, your test cases for the class-under-test are going to take a long time to run, and they're going to exercise already tested code.  What can you do?  Let's take a look at the original class:

   1: public class ComplicatedClass
   2: {
   3:     private LongRunningService mHelper;
   4:  
   5:     public ComplicatedClass()
   6:     {
   7:         mHelper = new LongRunningService();
   8:     }
   9:  
  10:     public void SimpleMethod()
  11:     {
  12:         Console.WriteLine("Beginning long running operation, this may take a while...");
  13:         mHelper.DoSomething();
  14:     }
  15: }

My recommendation is to use interfaces everywhere, and apply Dependency Injection.  When you apply Dependency Injection, the instantiator of your class becomes responsible for specifying which concrete dependencies to use.  When you combine this with interfaces, it becomes quite easy to mock access to external classes.  Here's our code, but modified so that we can apply Dependency Injection:

   1: public class DependentClass
   2:     {
   3:         private IService mHelper;
   4:  
   5:         public DependentClass()
   6:             : this(new LongRunningService())
   7:         {
   8:         }
   9:  
  10:         public DependentClass(IService helper)
  11:         {
  12:             mHelper = helper;
  13:         }
  14:  
  15:         public void SimpleMethod()
  16:         {
  17:             Console.WriteLine("Beginning long running operation, this may take a while...");
  18:             mHelper.DoSomething();
  19:         }
  20:     }
  21:  
  22:     public interface IService
  23:     {
  24:         void DoSomething();
  25:     }
  26:  
  27:     public class LongRunningService : IService
  28:     {
  29:         public void DoSomething()
  30:         {
  31:             Thread.Sleep(TimeSpan.FromSeconds(10));
  32:         }
  33:     }

To test the class without exercising LongRunningService, we create a private class that implements the IService interface, and pass that in through our unit tests, like so:

   1: private class TestService : IService
   2: {
   3:     public void DoSomething()
   4:     {
   5:         Console.WriteLine("In mock class!");
   6:     }
   7: }
   8:  
   9: [Test]
  10: public void SimpleMethod_DoesntExplode()
  11: {
  12:     DependentClass instance = new DependentClass(new TestService());
  13:     instance.SimpleMethod();
  14:  
  15: }

Notice how I've given DependentClass a default constructor that takes care of instantiating the "real" concrete implementation.  While this approach works, a more robust solution is to use an Inversion of Control Container, which we might explore in a future post.

Error-Handling Logic

A related testing problem is error-handling logic.  Error-handling logic for method parameters is quite easy to test (just pass in a bogus parameter from your test), but simulating error handling around an external resource, such as a web service, is much more difficult.  Let's take a look at a method from our old friend, the Account class:

   1: /// <summary>
   2: /// Transfers money to the specified account.
   3: /// </summary>
   4: /// <param name="targetAccount"></param>
   5: /// <param name="amount"></param>
   6: /// <returns></returns>
   7: public bool TransferMoney(int targetAccount, double amount)
   8: {
   9:     AccountService service = new AccountService("http://partner.bank/Management.asmx");
  10:  
  11:     if (!service.Transfer(targetAccount, amount))
  12:     {
  13:         Console.WriteLine("Failed to transfer!");
  14:         //TODO: Put additional error-handling logic here
  15:         return false;
  16:     }
  17:     else
  18:     {
  19:         return true;
  20:     }
  21: }
  22:  

How can we force our web service to fail so that we can verify our error-handling logic?  Well, we could have our test disable the network card in the machine or something like that, but there's an easier way.  Again, by using interfaces and dependency injection, we can easily simulate whatever error we desire. 

   1: public class Account : ICloneable
   2: {
   3:     ...
   4:     /// <summary>
   5:     /// The account service.
   6:     /// </summary>
   7:     private IAccountService mService;
   8:  
   9:     /// <summary>
  10:     /// Creates an account that will use the default service.
  11:     /// </summary>
  12:     public Account() : this(new AccountService("http://partner.bank/Management.asmx"))
  13:     {
  14:         
  15:     }
  16:  
  17:     /// <summary>
  18:     /// Creates an account that will use the specified service.
  19:     /// </summary>
  20:     /// <param name="service"></param>
  21:     public Account(IAccountService service)
  22:     {
  23:         mService = service;
  24:     }    
  25:     
  26:     /// <summary>
  27:     /// Transfers money to the specified account.
  28:     /// </summary>
  29:     /// <param name="targetAccount"></param>
  30:     /// <param name="amount"></param>
  31:     /// <returns></returns>
  32:     public bool TransferMoney(int targetAccount, double amount)
  33:     {
  34:         if (!mService.Transfer(targetAccount, amount))
  35:         {
  36:             Console.WriteLine("Failed to transfer!");
  37:             //TODO: Put additional error-handling logic here
  38:             return false;
  39:         }
  40:         else
  41:         {
  42:             return true;
  43:         }
  44:     }
  45:     
  46:     ...
  47: }
  48:  
  49: /// <summary>
  50: /// Interface that account services implement.
  51: /// </summary>
  52: public interface IAccountService
  53: {
  54:     bool Transfer(int account, double amount);
  55: }
  56:  
  57: /// <summary>
  58: /// The real account service.
  59: /// </summary>
  60: public class AccountService : IAccountService
  61: {
  62:     /// <summary>
  63:     /// .
  64:     /// </summary>
  65:     /// <param name="url"></param>
  66:     public AccountService(string url)
  67:     {
  68:         //TODO: real web service logic goes here.
  69:     }
  70:  
  71:     /// <summary>
  72:     /// .
  73:     /// </summary>
  74:     /// <param name="account"></param>
  75:     /// <param name="amount"></param>
  76:     /// <returns></returns>
  77:     public bool Transfer(int account, double amount)
  78:     {
  79:         //TODO: real webservice logic goes here.
  80:         return true;
  81:     }
  82: }

With the refactoring in place, all we have to do is create a test class that implements that interface, then pass that test object in to our class-under-test, like so:

   1: /// <summary>
   2: /// Used to facilitate testing of error-handling logic.
   3: /// </summary>
   4: private class TestAccountService : IAccountService
   5: {
   6:     /// <summary>
   7:     /// Simulates a bad transfer.
   8:     /// </summary>
   9:     /// <param name="account"></param>
  10:     /// <param name="amount"></param>
  11:     /// <returns></returns>
  12:     public bool Transfer(int account, double amount)
  13:     {
  14:         return false;
  15:     }
  16: }
  17:  
  18: /// <summary>
  19: /// Verifies that the method returns false if it can't communicate
  20: /// with the remote account service.
  21: /// </summary>
  22: [Test]
  23: public void Transfer_ReturnsFalseIfCommunicationFails()
  24: {
  25:     Account account = new Account(new TestAccountService());
  26: }

Sealed Dependencies

Dependency injection and interface-driven design can make testing simpler and save you a lot of headaches, but there are times when applying it may not seem so obvious.  Let's look at an example class that takes in log messages and sends an E-mail if the log looks like an error:

   1: /// <summary>
   2: /// Inspects log messages and sends E-mails if an error occurs.
   3: /// </summary>
   4: public class LogMonitor
   5: {
   6:     /// <summary>
   7:     /// Inspects log messages, and sends an E-mail if a message
   8:     /// contains the word "error".
   9:     /// </summary>
  10:     /// <param name="message"></param>
  11:     public void InspectLogMessage(string message)
  12:     {
  13:         if (message.ToUpper().Contains("ERROR"))
  14:         {
  15:             SmtpClient client = new SmtpClient();
  16:             client.Send("system@somewhere.com", "support@nospam.com", "Error!", message);
  17:         }
  18:     }
  19: }

To fully test this class, we need to verify that an E-mail is sent when an error message is passed in.  SmtpClient is a sealed class that we don't have access to, and it doesn't have an interface, so we can't directly apply dependency injection.  Fortunately, there is a simple pattern we can apply to solve our problem: the Adapter Pattern.  First, we create an interface that abstracts the parts of SmtpClient that our application uses, like so:

   1: /// <summary>
   2: /// Wrapper interface for <see cref="SmtpClient"/>.
   3: /// </summary>
   4: public interface ISmtpClient
   5: {
   6:     /// <summary>
   7:     /// Sends an E-mail.
   8:     /// </summary>
   9:     /// <param name="from"></param>
  10:     /// <param name="recipients"></param>
  11:     /// <param name="subject"></param>
  12:     /// <param name="body"></param>
  13:     void Send(string from, string recipients, string subject, string body);
  14: }

Next, we create an adapter class that wraps SmtpClient into our interface:

   1: /// <summary>
   2: /// Adapts <see cref="SmtpClient"/> into the <see cref="ISmtpClient"/>
   3: /// interface.
   4: /// </summary>
   5: public class SmtpClientAdapter : ISmtpClient
   6: {
   7:     /// <summary>
   8:     /// Sends an E-mail.
   9:     /// </summary>
  10:     /// <param name="from"></param>
  11:     /// <param name="recipients"></param>
  12:     /// <param name="subject"></param>
  13:     /// <param name="body"></param>
  14:     public void Send(string from, string recipients, string subject, string body)
  15:     {
  16:         SmtpClient client = new SmtpClient();
  17:         client.Send(from, recipients, subject, body);
  18:     }
  19: }

Finally, we apply dependency injection to our class-under-test, and update our test cases, passing in a mock that we can use to verify that a mail message was "sent".

LogMonitor:

   1: /// <summary>
   2: /// Inspects log messages and sends E-mails if an error occurs.
   3: /// </summary>
   4: public class LogMonitor
   5: {
   6:     /// <summary>
   7:     /// The client used to send E-mail
   8:     /// </summary>
   9:     private ISmtpClient mClient;
  10:  
  11:     /// <summary>
  12:     /// Creates a monitor that will use the default SMTP client.
  13:     /// </summary>
  14:     public LogMonitor() : this(new SmtpClientAdapter())
  15:     {
  16:         
  17:     }
  18:  
  19:     /// <summary>
  20:     /// Creates a monitor that will use the specified 
  21:     /// <see cref="ISmtpClient"/>.
  22:     /// </summary>
  23:     /// <param name="smtpClient"></param>
  24:     public LogMonitor(ISmtpClient smtpClient)
  25:     {
  26:         mClient = smtpClient;
  27:     }
  28:  
  29:     /// <summary>
  30:     /// Inspects log messages, and sends an E-mail if a message
  31:     /// contains the word "error".
  32:     /// </summary>
  33:     /// <param name="message"></param>
  34:     public void InspectLogMessage(string message)
  35:     {
  36:         if (message.ToUpper().Contains("ERROR"))
  37:         {
  38:             mClient.Send("system@somewhere.com", "support@nospam.com", "Error!", message);
  39:         }
  40:     }
  41: }

And LogMonitorTests:

   1: /// <summary>
   2: /// Tests fixture for <see cref="LogMonitor"/>.
   3: /// </summary>
   4: [TestFixture]
   5: public class LogMonitorTests
   6: {
   7:     /// <summary>
   8:     /// Test class.
   9:     /// </summary>
  10:     private class TestSmtpClient : ISmtpClient
  11:     {
  12:         /// <summary>
  13:         /// Incremented every time a message is sent.
  14:         /// </summary>
  15:         public int MessagesSent { get; private set; }
  16:  
  17:         /// <summary>
  18:         /// Records that a message was sent.
  19:         /// </summary>
  20:         /// <param name="from"></param>
  21:         /// <param name="recipients"></param>
  22:         /// <param name="subject"></param>
  23:         /// <param name="body"></param>
  24:         public void Send(string from, string recipients, string subject, string body)
  25:         {
  26:             Console.WriteLine("Received message: {0}", body);
  27:             MessagesSent++;
  28:         }
  29:     }
  30:  
  31:     /// <summary>
  32:     /// Verifies that the method correctly sends an E-mail 
  33:     /// when it receives an error log.
  34:     /// </summary>
  35:     [Test]
  36:     public void InspectLogMessage_SendsErrorEmail()
  37:     {
  38:         TestSmtpClient client = new TestSmtpClient();
  39:  
  40:         LogMonitor monitor = new LogMonitor(client);
  41:  
  42:         monitor.InspectLogMessage("This is an error.");
  43:  
  44:         Assert.AreEqual(1, client.MessagesSent);
  45:     }
  46: }

Database Access Code

The mother of all external dependencies is database access code.  How do you test code that must interact with a database?  In some cases, we can apply what we've learned (dependency injection, the adapter pattern, etc), but those solutions won't scale well to a complicated system with a lot of data access logic, and if you are trying to mock classes from the System.Data.Sql namespace, You Are Doing It Wrong.  You can't just let your classes-under-test access the real database, either, because that's a shared resource; changes made in one test will be visible in another, a clear violation of the principles of unit testing.  Using a real database heavily in your unit tests will also guarantee that your tests will take forever to run.  Remember that unit tests should execute quickly (and consistently), so that approach is not viable.

Unfortunately, I have yet to find a good solution to this problem.  I have taken two approaches in the past, both of which have some downsides.  One approach was to wrap all data access code in an interface.  We then created two classes that implemented that interface: one was real (it actually talked to SQL Server), and the other was a horrible mess of code that was basically an in-memory database.  Aside from having to maintain a complex code base to facilitate testing (our in-memory version is about as many lines of code as our real SQL version), developing and testing against the in-memory version led to a lot of problems.  The in-memory version didn't always enforce the same constraints that the real version would, so we would frequently run into things that worked fine in testing but failed in production. 

If you are using an ORM, your options are slightly better.  With Castle ActiveRecord or NHibernate, you can use SQLite, and you get mostly equivalent functionality to a real SQL Server database.  You also have less code to test since most of the common CRUD stuff has been done for you.  This approach can be problematic to setup and maintain, but it's the best solution I've found so far.  You can find out more about how to set that up with ActiveRecord in my post on Easy testing via ActiveRecord and SQLite.

GUIs

Finally, one of the most commonly asked unit testing question is "how do I unit test my GUI?"  My answer is "you don't."  Your code should be structured so that the code-behind for your GUI contains a *minimal* amount of logic.  Regardless of whether we're talking WinForms or WebForms, your GUI should contain only the logic dealing specifically with presentation (layout and formatting), and nothing more.  Anything beyond that should go into "middle tier" classes, classes that can easily be tested.  Here's an example for a BAD event handler:

   1: private void OrderButton_Clicked(object sender, EventArgs e)
   2: {
   3:     string item = ItemTextBox.Text;
   4:     int quantity = int.Parse(QuantityTextBox.Text);
   5:  
   6:     SqlConnection connection = new SqlConnection(mConnectionString);
   7:  
   8:     SqlCommand command = new SqlCommand("INSERT INTO Orders(Item,Quantity) VALUES(@Item,@Quantity)", connection);
   9:     command.Parameters.AddWithValue("Item", item);
  10:     command.Parameters.AddWithValue("Quantity", quantity);
  11:  
  12:     connection.Open();
  13:  
  14:     int resultCount = command.ExecuteNonQuery();
  15:  
  16:     if (resultCount == 0)
  17:     {
  18:         ResultLabel.Text = "No rows were inserted!";
  19:     }
  20:     else
  21:     {
  22:         ResultLabel.Text = "Database updated!";
  23:     }
  24: }

And here's what the event handler looks like once you've refactored it:

   1: private void OrderButton_Clicked(object sender, EventArgs e)
   2: {
   3:     OrderProcessor processor = new OrderProcessor();
   4:  
   5:     if (!processor.HandleNewOrder(ItemTextBox.Text, QuantityTextBox.Text))
   6:     {
   7:         ResultLabel.Text = "No rows were inserted!";
   8:     }
   9:     else
  10:     {
  11:         ResultLabel.Text = "Database updated!";
  12:     }
  13: }

If you absolutely HAVE to write test cases for a GUI, there are toolkits that can help.  I have very little/no experience with these, so use at your own risk (and remember, good design is a almost always a better answer):

If you have experience with any of these tools, please chime in below and let myself and others know what you think of them.

Up Next...

In today's post, I've gone through some of the commonly-encountered unit testing problems, and I've hopefully shown you how to address them.  In the next post, we'll look at how to take make it easier to apply dependency injection and the adapter pattern through the use of the excellent Moq mocking framework.  Look for that post on Friday at the earliest.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Unit Testing in .NET Part 3 - Asserting That Your Code Rocks

clock December 16, 2008 07:52 by author Matt

In the previous entries in this series, you've learned about the basics of unit testing, and you've seen how to create a very basic unit test.  In this post, you will learn how to fully use NUnit's Assert class to create a full suite of unit tests.  This post builds off the sample described in the previous post, so be sure to check it out if you want to follow along.

Asserting Equality

In the last post, you saw one example of how to use the Assert.AreEqual method to verify that two objects are equal.  As you will soon see, most methods on the Assert class have a ton of overloads.  You can think of the method as having two levels of overloads: the first for all the various types you could pass in (it has specific overloads for most primitive types as well as more generic versions that work with anything that derives from object), and the second level for controling the message that is shown when the Assert fails.  The type-based overloads are self-explanatory and are handled by the compiler for you automatically, so we'll ignore those and focus on the overloads that look like Assert.AreEqual(expected,actual,message) and Assert.AreEqual(expected,actual,message,params).  Let's incorporate one of these overloads into our unit test from the last post.  Here's the original, unmodified test:

   1: /// <summary>
   2: /// Verifies that the balance increases
   3: /// by the appropriate amount.
   4: /// </summary>
   5: [Test]
   6: public void Deposit_AddsValueToBalance()
   7: {
   8:     Account account = new Account();
   9:     //account.Balance is currently zero.
  10:     account.Deposit(100);
  11:   
  12:     Assert.AreEqual(100, account.Balance);
  13: }

Let's change our assumption about what the Deposit method should be doing.  Let's say that this is an awesome bank that automatically adds a 10% match to anything that you deposit.  We could just change the first parameter to Assert.AreEqual to 110, but what happens if we run that test?  All you will see is the 'expected (110), actual (100)' message.  It doesn't tell you much about why 110 was expected.  That's where the third parameter comes in handy:

   1: /// <summary>
   2: /// Verifies that the balance increases
   3: /// by the appropriate amount.
   4: /// </summary>
   5: [Test]
   6: public void Deposit_AddsValueToBalance()
   7: {
   8:     Account account = new Account();
   9:     //account.Balance is currently zero.
  10:     account.Deposit(100);
  11:  
  12:     Assert.AreEqual(110, account.Balance, "Deposit bonus was not applied!");
  13: }

Now when you run the test, you will see the helpful message that was supplied as the third parameter to AreEqual.  The fourth parameter, a params array of objects, behaves like the String.Format method: the third parameter becomes a format string, and the fourth parameter is the set of values to insert into the format string.  This could be useful for logging additional information about the unit test failure.

For both double and float types, the AreEqual method has an additional set of overloads that look like Assert.AreEqual(expected,actual,tolerance).  The tolerance parameter allows you to tell NUnit how close two floating-point values have to be in order to be considered equal.  Going back to our Deposit_AddsValueToBalance test, what if the bonus wasn't *quite* 10%, but was more like 9.75989%?  Well, we could calculate exactly what the bonus would be and pass that in as the expected value, then apply it, or we could specify a tolerance of 0.01 and leave the expected value as 110, like so:

   1: /// <summary>
   2: /// Verifies that the balance increases
   3: /// by the appropriate amount.
   4: /// </summary>
   5: [Test]
   6: public void Deposit_AddsValueToBalance()
   7: {
   8:     Account account = new Account();
   9:     //account.Balance is currently zero.
  10:     account.Deposit(100);
  11:  
  12:     Assert.AreEqual(110, account.Balance, 0.01, "Deposit bonus was not applied!");
  13: }

In addition to primitive types, NUnit has some "special" support for Arrays and Collections.  Typically in the .NET world, Equality is determined by an object's Equals method. Derived types are responsible for overriding that method if they wish to define equality as anything other than the default behavior inherited from the object class.  NUnit fudges this definition a bit for Collections and Arrays: two collections (or arrays) are considered equal if they have the same number of items and all their corresponding elements are equal. 

There is a corresponding inverse method to AreEqual called (not surprisingly) AreNotEqual.  The obvious difference is that AreNotEqual verifies that two objects are different from one another.  AreNotEqual has the same set of overloads as its complementary method.

Asserting Sameness

Next up is the AreSame method.  You may be wondering "what's the difference between 'same' and 'equal'?".  Objects A and B are equal if A.Equals(B) returns true.  Remember that by default all objects inherit an Equals method from the base object class, and that derived classes can implement custom equality checks as needed, so the exact definition of equal depends on what you are comparing.  'Same' is much simpler: objects  A and B are the same if they point to the exact same object in memory. 

The difference between 'equal' and 'same' may sound a bit confusing if you aren't comfortable with the concept of pointers and object references, so check the documentation here if you are still unclear.

To demonstrate this difference, let's create a new method that looks up account information and verify that repeated calls to the method return the same account information instance.  NOTE: I am intentionally not doing things in a test-driven manner right now.  I don't want to muddy the waters with trying to explain that concept at the same time I'm explaining the asserts.  A proper treatment of test-driven development is coming Real Soon(tm)!

First, let's add some new properties to our Account class along with an overloaded Equals method:

   1: /// <summary>
   2: /// A bank account.
   3: /// </summary>
   4: public class Account
   5: {
   6:     #region Public Properties
   7:  
   8:     /// <summary>
   9:     /// The ID of the account.
  10:     /// </summary>
  11:     public int AccountID { get; private set; }
  12:  
  13:     /// <summary>
  14:     /// The name of the account owner.
  15:     /// </summary>
  16:     public string Owner { get; private set; }
  17:  
  18:     /// <summary>
  19:     /// The current account balance.
  20:     /// </summary>
  21:     public float Balance { get; private set; }
  22:  
  23:     #endregion
  24:  
  25:     #region Public Methods
  26:     
  27: ----Snip----
  28:  
  29:     /// <summary>
  30:     /// Compares the current object to the specified object.
  31:     /// </summary>
  32:     /// <param name="obj"></param>
  33:     /// <returns>True if the accounts have the same AccountID,
  34:     /// false otherwise.</returns>
  35:     public override bool Equals(object obj)
  36:     {
  37:         Account account = obj as Account;
  38:  
  39:         if (account == null)
  40:         {
  41:             return false;
  42:         }
  43:         else
  44:         {
  45:             return AccountID == account.AccountID;
  46:         }
  47:     }
  48:  
  49:     /// <summary>
  50:     /// When you override Equals, you have to override
  51:     /// GetHashCode, too...
  52:     /// </summary>
  53:     /// <returns></returns>
  54:     public override int GetHashCode()
  55:     {
  56:         return AccountID.GetHashCode();
  57:     }
  58:  
  59:     #endregion
  60: }

Next, let's add a method to look up and return a 'dummy' Account object on demand:

   1: /// <summary>
   2: /// Gets the specified account.
   3: /// </summary>
   4: /// <param name="accountID"></param>
   5: /// <returns></returns>
   6: public static Account Lookup(int accountID)
   7: {
   8:     return new Account {AccountID = accountID, Owner = "John Doe"};
   9: }

Finally, let's create our test:

   1: /// <summary>
   2: /// The Lookup should return the exact same instance
   3: /// for all lookups on a specific ID.
   4: /// </summary>
   5: [Test]
   6: public void Lookup_ReturnsSameInstance()
   7: {
   8:     Account account = Account.Lookup(1);
   9:  
  10:     Assert.AreSame(account, Account.Lookup(1));
  11: }

Go ahead and build the project and run the test.  What happened?  The test failed because even though we are returning an identical object for both calls, we aren't actually returning the same object.  Let's correct that by storing some static dummy Account instances; we'll return one of these instead of creating a new instance from now on:

   1: /// <summary>
   2: /// These are our dummy accounts.
   3: /// </summary>
   4: private static Account[] mAccounts = new Account[]
   5: {
   6:     new Account{ AccountID = 1, Owner = "John Doe" },
   7:     new Account{ AccountID = 2, Owner = "Jane Doe" }
   8: };
   9:  
  10: /// <summary>
  11: /// Gets the specified account.
  12: /// </summary>
  13: /// <param name="accountID"></param>
  14: /// <returns></returns>
  15: public static Account Lookup(int accountID)
  16: {
  17:     //Arrays are 0-based, accountIDs are 1-based, so we shift them.
  18:     return mAccounts[accountID - 1];
  19: }

Build and re-run the test, and you should get a success message.

Similar to the AreEqual method, the AreSame method has a logical inverse: the AreNotSame method. 

Asserting Greatness

NUnit includes four methods (with overloads) for asserting various inequalities: Greater, GreaterOrEqual, Less, and LessOrEqual.  The intent of these methods should be obvious, but they differ in one major one from the other assertions we've seen so far.  Recall that the basic versions of both AreEqual and AreSame took an expected argument first and the actual value second.  Applying that same logic, you might expect that expressing the inequality x is greater than y would look like Assert.Greater(y,x), but it's actually the opposite.  I can't tell you how many times I've seen this inconsistency bite developers; it doesn't help that the parameters have less-than-helpful names, like arg1 and arg2.  I don't know why they couldn't have used something more obvious and intuitive, like maybe left and right...

Enough complaining, Let's write some code!  Let's add a new deposit to our account class called RandomDeposit.  This method is very different from our standard Deposit method.  Instead of depositing the specified amount, RandomDeposit will deposit a random amount that is anywhere from 0.5 to 1.5 times the specified amount.  The method looks like so:

   1: /// <summary>
   2: /// Deposits a random amount that is between 0.5 
   3: /// and 1.5 times the specified amount.
   4: /// </summary>
   5: /// <param name="amount"></param>
   6: public void RandomDeposit(float amount)
   7: {
   8:     Random rand = new Random();
   9:     double multiplier = rand.NextDouble();
  10:  
  11:     Balance += (float)(amount*(0.5 + multiplier));
  12: }

Because of the randomness in the method, it's going to be very hard to write a unit test using the Assert.AreEqual method.  Instead, we'll use the GreaterOrEqual method to assert that the deposited amount is at least 0.5 times the amount we deposited.  Here's the unit test:

   1: /// <summary>
   2: /// The method should deposit between 0.5 and 1.5
   3: /// times the specified amount.
   4: /// </summary>
   5: [Test]
   6: public void RandomDeposit_DepositsExpectedAmount()
   7: {
   8:     Account account = new Account();
   9:  
  10:     account.RandomDeposit(100);
  11:     Assert.GreaterOrEqual(account.Balance, 50);
  12:     Assert.LessOrEqual(account.Balance, 150);
  13: }

If you aren't already building and testing by habit, go ahead and build the project and run the new test. 

Asserting Typeiness (If Steven Colbert can do it, so can I!)

The Assert class includes methods for asserting things about the type of an instance.  You can check whether or not an object is of a given type using the IsInstanceOfType method.  The first parameter is the expected type of the object, the second parameter is the actual object.  Let's make our Account class implement the ICloneable interface, then write a test to verify that our clone actually is of type Account:

   1: /// <summary>
   2: /// A bank account.
   3: /// </summary>
   4: public class Account : ICloneable
   5: {
   6: ----SNIP----
   7: /// <summary>
   8: /// Clones the current account.
   9: /// </summary>
  10: /// <returns></returns>
  11: public object Clone()
  12: {
  13:     return new Account {AccountID = AccountID, Balance = Balance, Owner = Owner};
  14: }
  15: ----SNIP----
  16: }

Here's the corresponding test case:

   1: /// <summary>
   2: /// Verifies that a complete clone of the account
   3: /// is returned.
   4: /// </summary>
   5: [Test]
   6: public void Clone_ReturnsAccountClone()
   7: {
   8:     Account account = Account.Lookup(1);
   9:  
  10:     object clone = account.Clone();
  11:  
  12:     Assert.IsInstanceOfType(typeof (Account), clone);
  13: }

The IsInstanceOfType and IsAssignableFrom methods are very similar.  Under the covers, they're just calling the corresponding members of the System.Type class.  Check the documentation on MSDN if you are curious about the subtle differences between the two methods, but for the most part, you can use them interchangeably. 

As with most assert methods, there are various overloads of both IsAssignableFrom and IsInstanceOfType.  Each also has a set of complementary Not methods: IsNotAssignableFrom and IsNotInstanceOfType.

Asserting Nothingness

Sometimes the right result is a null result.  Let's look again at our Account.Lookup method.  Right now, we're not really handling the case of an account ID that doesn't exist.  Let's modify the code so that it returns null when given an invalid account ID:

   1: /// <summary>
   2: /// Gets the specified account.
   3: /// </summary>
   4: /// <param name="accountID"></param>
   5: /// <returns></returns>
   6: public static Account Lookup(int accountID)
   7: {
   8:     if (accountID < 1 || accountID > mAccounts.Length)
   9:     {
  10:         return null;
  11:     }
  12:  
  13:     //Arrays are 0-based, accountIDs are 1-based, so we shift them.
  14:     return mAccounts[accountID - 1];
  15: }

And let's write a new test case to verify this:

   1: /// <summary>
   2: /// The lookup should return null when given an ID
   3: /// that doesn't correspond to an account.
   4: /// </summary>
   5: [Test]
   6: public void Lookup_ReturnsNullForInvalidId()
   7: {
   8:     Assert.IsNull(Account.Lookup(0));
   9:  
  10:     Assert.IsNotNull(Account.Lookup(2));
  11: }

Here we've used the Assert.IsNull method.  This is a very simple assert: it simply checks that the parameter is null.  Like everything else, it has a complementary method that will test that something is not null. 

Asserting Truthiness (or Falsiness)

Everything we've asserted so far could actually be expressed using one of the most basic assertions: IsTrue.  This method asserts that a boolean input is true.  It has a complementary IsFalse method that can be used to assert that an input is false.  These methods can be used to test anything that you can express as a boolean condition.  Let's rewrite our previous Lookup test using only IsTrue instead of IsNull to see this:

   1: /// <summary>
   2: /// The lookup should return null when given an ID
   3: /// that doesn't correspond to an account.
   4: /// </summary>
   5: [Test]
   6: public void Lookup_ReturnsNullForInvalidId()
   7: {
   8:     Assert.IsTrue(Account.Lookup(0) == null);
   9:     //Assert.IsNull(Account.Lookup(0));
  10:  
  11:     Assert.IsTrue(Account.Lookup(2) != null);
  12:     //Assert.IsNotNull(Account.Lookup(2));
  13: }

The test should produce identical output because it is logically equivalent to the original.  You might be tempted to just say "forget about all these other asserts, I'll just use IsTrue for everything!", but that's a terrible idea.  The various other assertions give you a lot more information when something goes wrong than IsTrue will.  For example, if GreaterThan fails, it will tell you the values of both parameters.  If you expressed the test using only IsTrue, you would get a very unhelpful message that says "Expected: True, Actual: False".  Sure, you can probably work backwards, add some logging, etc, to figure out what's going on, but why not use the more powerful GreaterThan method to begin with?

Asserting Failuriness

Sometimes you just want a test to fail. Maybe the test isn't finished, or the test couldn't perform some setup correctly, or maybe you need to test for something that is beyond what the built-in assertion methods can handle.  Assert.Fail to the rescue!  Calling this method will instantly fail a test (assuming you haven't done anything silly like wrapped the call with a try-catch block, which we will look at in a future post). 

Asserting Exceptioniness

We've tested that things work so far, but how do we test that things explode?  Right now, there's nothing in our Deposit method that prevents us from depositing negative amounts.  Let's add some logic to throw an exception:

   1: /// <summary>
   2: /// Deposits the specified amount.
   3: /// </summary>
   4: /// <param name="amount"></param>
   5: public void Deposit(float amount)
   6: {
   7:     if (amount <= 0)
   8:     {
   9:         throw new ArgumentOutOfRangeException("amount", amount, "Must be greater than zero.");
  10:     }
  11:  
  12:     Balance += amount;
  13: }

Error-handling code is good, but it still needs to be tested.  NUnit has the ExpectedException attribute that you can use to verify that an exception is thrown, but I hate this attribute.  What the attribute is really doing is verifying that something somewhere in your test case is throwing an exception, not that the exception is actually coming from where you want it to come from.  Instead, I prefer to go with this model:

   1: /// <summary>
   2: /// The method should throw an ArgumentOutOfRangeException
   3: /// if you pass in a negative value.
   4: /// </summary>
   5: [Test]
   6: public void Deposit_ThrowsExceptionOnNegativeAmount()
   7: {
   8:     Account account = new Account();
   9:  
  10:     try
  11:     {
  12:         account.Deposit(-100);
  13:         //The following line will only be executed if the Deposit method
  14:         //failed to throw an exception.
  15:         Assert.Fail("Expected ArgumentOutOfRangeException was not thrown!");
  16:     }
  17:     catch (ArgumentOutOfRangeException)
  18:     {
  19:         //Ok, this is an expected exception.
  20:     }
  21: }

It requires a bit more code, but this version verifies that the correct type of exception is thrown in exactly the right spot.

Other Ways to Assert RoXXorness

The methods we've looked at so far are just the ones that I have found myself using often over the last several years.  NUnit includes other methods that you can use to assert various things about your objects, including:

  • Assert.Contains - Given an object and a list, this method asserts that the collection contains the specified object.
  • Assert.IsEmpty - Given a collection (or a string), asserts that the object contains no items.
  • Assert.IsNaN - Both double.NaN represent the 'not-a-number' condition (often caused by division by zero).  You can test for this condition using the IsNaN assert.

There is even more...

Recent versions of NUnit have added additional utilities, asserts, etc. to simplify your testing.  You can find out more about them here.  We might look at those in a future post, but I really don't find myself using most of them in my day-to-day testing, and I think most developers can get by just fine without them.

In the next post in this series, we'll look at some more complicated testing scenarios as well as common testing problems and strategies for overcoming them.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Unit Testing in .NET Part 2 - Your First Unit Tests

clock December 10, 2008 04:36 by author Matt

In the last post, you learned the basics of unit testing in .NET: you should understand the intent behind unit testing, you should have a vague idea of what unit tests should look like, and you should have looked at some of the unit testing API options that are available to you.  For the remainder of this series, I'll be focusing specifically on unit testing with NUnit and TestDriven.NET.  If you have chosen to use a different API or tool, you may need to translate the code samples and steps below to match.  If you don't already have NUnit downloaded and installed, do so now.

Adding NUnit To An Existing Project

For now, we'll assume that we are adding unit tests for a project that has already been created.  We'll deal with test-driven development (not to be confused with the test-running tool by the same name) in a future post.  Our tests will be built around an example taken from the NUnit Quick Start, where we have a simple bank API that includes a single Account class.  Here's the code for the class:

   1: using System;
   2:  
   3: namespace NunitDemo
   4: {
   5:     /// <summary>
   6:     /// A bank account.
   7:     /// </summary>
   8:     public class Account
   9:     {
  10:         /// <summary>
  11:         /// The current account balance.
  12:         /// </summary>
  13:         public float Balance { get; private set; }
  14:  
  15:         /// <summary>
  16:         /// Withdraws the specified amount from the account.
  17:         /// </summary>
  18:         /// <param name="amount"></param>
  19:         public void Withdraw(float amount)
  20:         {
  21:             Balance -= amount;
  22:         }
  23:  
  24:         /// <summary>
  25:         /// Deposits the specified amount.
  26:         /// </summary>
  27:         /// <param name="amount"></param>
  28:         public void Deposit(float amount)
  29:         {
  30:             Balance += amount;
  31:         }
  32:  
  33:         /// <summary>
  34:         /// Transfers money to the specified account.
  35:         /// </summary>
  36:         /// <param name="destination"></param>
  37:         /// <param name="amount"></param>
  38:         public void TransferFunds(Account destination, float amount)
  39:         {
  40:             throw new NotImplementedException();
  41:         }
  42:  
  43:     }
  44: }

Create a new project in Visual Studio named 'NunitDemo', add this class to it, and verify that your project builds correctly.  If all goes well, you are now ready to add NUnit to the project.  Assuming you are using Visual Studio, find the "References" folder under the project in the "Solution Explorer" pane.  Right-click on "References" and choose "Add Reference".  From the .NET tab of the "Add Reference" dialog, choose "nunit.framework", then click "Ok".  This DLL contains the attributes and assertion functionality that you need to create your actual unit tests.  At this point, you should see "nunit.framework" listed under the "References" folder.  If so, you are now ready to begin writing test cases!

Sidetrack - Where Do Unit Tests Go?

Before we actually start writing test cases, let's talk about where we're going to put them.  The conventional approach is to put test cases in a completely separate project from the code being tested.  In our example, if our Account class is in a project named "BankSystem", our unit tests would go in a completely separate project called "BankSystem.Tests".  I have two big problems with this approach.  First, it bloats the size of your Visual Studio solution, which means it will take longer to build the solution, and you have twice the number of overhead files (the project file, the documentation file, all the little temp files and folders that Visual Studio and Resharper create, etc).  Second, what if you need to test a class or method that's marked as internal?  You can't do that if your tests live in a completely separate project.

My recommendation is to put the tests in the same project as the code being tested, but do separate the tests into their own namespaces.  For example, our "Account" class is in the "NunitDemo" namespace, so its tests will go in the "NunitDemo.Tests" namesapce.  The downside to this approach is that your test cases will now be distributed along with your actual API, but there's a very easy solution to this: wrap the tests with preprocessor directives.  Depending on your environment and how you deploy your production assemblies, it may be sufficient to simply test whether the DEBUG symbol is defined.  This symbol is defined by default when you build a project in debug mode in Visual Studio.  If you sometimes deploy debug versions of code, then you may want to define a custom symbol to use instead.  For now, we'll assume that we want our unit tests compiled if the DEBUG symbol is defined. 

Writing Your First NUnit Test

At this point, your project should have a reference to nunit.framework, and you should know where you are going to put the test cases.  Let's go ahead and create a test fixture for the Account class.  Within Visual Studio solution explorer, create a new folder called "Tests".  Folders in Visual Studio are typically used to denote namespaces, so any classes you add to this folder will be created in the "NunitDemo.Tests" namespace by default (you can change the namespace after the fact, but that would be WRONG).  Right-click on the new folder, click "Add", then select "Class".  Name the class "AccountTests".  Visual Studio should create the class and open the .cs file for you.  This is just a standard class right now.  We have to make a few changes in order for it to become a test fixture.

First, let's wrap everything in a '#if DEBUG' directive (see the code below if you aren't sure how to do that).  This insures that our new class will only be compiled if the DEBUG directive is defined.  Next, add a using statement for the NUnit.Framework namespace.  NUnit requires that test fixtures be public classes, so go ahead and add the 'public' keyword to the class definition.  Finally, apply the "TestFixture" attribute to the class.  You now have an empty test fixture like so:

   1: #if DEBUG
   2:  
   3: using System;
   4: using System.Collections.Generic;
   5: using System.Linq;
   6: using System.Text;
   7: using NUnit.Framework;
   8:  
   9: namespace NunitDemo.Tests
  10: {
  11:     /// <summary>
  12:     /// Test fixture for <see cref="Account"/>.
  13:     /// </summary>
  14:     [TestFixture]
  15:     public class AccountTests
  16:     {
  17:     }
  18: }
  19:  
  20: #endif

Let's go ahead and add our first test case.  We're going to create a test to verify that the deposit method correctly adds money to the account balance.  Create a method named "Deposit_AddsValueToBalance".  Yes, typically underscores in method names are BAAAD, but this isn't a normal method, it's a test case.  When a test case fails, you want to know immediately what method failed and what the test was supposed to verify, so you need to use descriptive names.  Using underscores allows you to clearly indicate the name of the method being tested and the behavior being tested.  Anyway, you've created your method, now mark it with the "Test" attribute.  Congratulations, you now have an empty test case!  You could run the test now, and it would pass (because it doesn't do anything), but let's add some meat to it first.

Before we write the logic for the test, let's think about exactly what we want to test: we should be able to call the Deposit method, passing in some value X, and the balance should be incremented by that same value X.  How do we test that?  First, we call the method, passing in some constant value.  Next, we use the Assert class from NUnit to verify that the balance increased by that amount.  There are two ways we could do that in NUnit: we can use the so-called "classic model", or we could use the newer "constraint-based" model.  I'm still on the fence about which one I prefer, but for now, we'll stick with the classic model, which means we're going to use the Assert.AreEqual method to check the value of the Balance property after we have invoked the Deposit method.

That's a long paragraph.  If you're confused, maybe this will help:

   1: /// <summary>
   2: /// Verifies that the balance increases
   3: /// by the appropriate amount.
   4: /// </summary>
   5: [Test]
   6: public void Deposit_AddsValueToBalance()
   7: {
   8:     Account account = new Account();
   9:     //account.Balance is currently zero.
  10:     account.Deposit(100);
  11:  
  12:     Assert.AreEqual(100, account.Balance);
  13: }

And that's our test case.  Simple, isn't it?  We're writing code to test code, utilizing the Assert class to verify that expected things are actually happening.  The Assert.AreEqual call will throw an exception if for some reason the Balance property isn't equal to 100.  Assuming you are using TestDriven.NET, you can run your test by right-clicking on either the name of the test method or the name of the fixture class in the code window, then selecting "Run Tests" from the context menu.  TestDriven.NET will launch in the background, run your tests, then spit out the results in the Output pane within Visual Studio.

EOF

So far, we've tested a single, simple method using a single method from the Assert class.  Unfortunately, testing won't always be this simple and straightforward.  In the next post, we'll begin creating more complex test cases as we try out different Assert methods.  We'll also look at some of the other attributes that NUnit supports and how they can help you create tests more efficiently. 

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Unit Testing in .NET Part 1 - Introduction to Testing

clock December 9, 2008 04:43 by author Matt

Today is the first 'requested topic' post.  If you have a topic you would like to hear more about, drop me a line, and if it's something that I'm either interested in or working with anyway, I'll try to give the topic a proper post.  Serious suggestions only (yes, I'm talking to you, Evil Rob). 

Introduction

Today's post kicks off a series that's all about unit testing.  This series assumes no prior knowledge of unit testing, so it may start out a tad too basic for some, but it will carry all the way in to Test Driven Development. 

Let's start out with some background.  Software testing is simply the act of verifying the behavior of a piece of software.  The goal of testing is to uncover bugs (aka defects) so that they can be evaluated and possibly corrected.  Software testing is the bane of many coders' existence; that's one of the things that makes them coders instead of developers.  Testing is not like 'coding'.  You have to approach it with a different mindset.  Instead of trying to implement a feature, you are trying to break something.  Testing can be depressing.  No matter how much you test, you will probably never uncover all the bugs in anything more complicated than a 'Hello World' application.  Still, studies have consistently shown that testing improves the quality of software systems, reduces maintenance costs, and generally makes life better and more fun once you know how to do it correctly

There are numerous types of testing (and various ways to categorize all the different types).  An exhaustive list is beyond the scope of this post, but here are some of the more common types you may encounter:

  • Unit Testing - Tests that target a specific method or class, usually created by a single person or a small team.
  • Component Testing - Like unit testing, but taken up to the API or component level.
  • Integration Testing - Testing the component pieces together as an integrated system.  This type of testing can be difficult due to external dependencies, such as databases, web services, other network resources, etc.
  • Regression Testing - Re-running previous test cases to verify that no new defects have been introduced that break previously-working functionality.
  • System Testing - Testing the software system in its near-final configuration (beta testing could be considered a type of System Testing).
  • Load Testing - 'Loading' a system with work (such as traffic, method calls, etc.) to evaluate how it behaves under normal workloads. 
  • Stress Testing - Like load testing, except with workloads elevated beyond what are considered normal or expected levels.
  • Security Testing - Testing specifically for security exploits and vulnerabilities.

That's a long (but not exhaustive) list, and while each is probably worthy of a series of posts, I think the lowest-hanging fruit is unit testing.  It's easy to get started with (as we'll see today), enables regression testing, and has great tool support in the .NET world.  So, on to unit testing!

Unit Testing

The purpose of unit testing is to verify that a specific unit of code works as intended.  By 'unit of code', I mean a method or perhaps a class, but certainly nothing larger than that.  Typically, you will create a suite of tests for a library.  A suite is composed of multiple fixtures, which themselves are composed of multiple test cases.  Typically, a suite contains fixtures for an individual library, a fixture contains tests for an individual class, and a test case verifies one specific bit of functionality from a method.  A method should usually have multiple test cases.  You want each test case to verify a specific behavior of the method.  Take the following method as an example:

   1: public void DividePositiveNumberByTen(int number)
   2: {
   3:     if (number < 1)
   4:         throw new ArgumentOutOfRangeException();
   5:     else
   6:         return number / 10;
   7: }

Notice that the method actually does two things: if the parameter is less than 1, it throws an exception, otherwise it returns the result of dividing the number by 10.  You should create two test cases for this method, one that verifies that the method throws an exception when you pass 0 as an input, and the other that verifies that it returns the correct value for a valid input.  (We'll get in to the specifics of *how* you create those tests in the next post.)  In general, you want to test each distinct path of execution through your code.  This keeps your tests simple and clean, plus if a test fails, you will know exactly what is broken.

Unit Testing in .NET

The .NET world has almost a one-to-one ratio between developers and unit testing frameworks.  Some of the more popular ones are MbUnit, MS Test, and NUnit.  I started unit testing with NUnit many moons ago, and I've never found myself motivated enough to switch, so, I'm still using NUnit.  When coupled with TestDriven.NET, you can run your tests directly from within Visual Studio, which makes you more likely to actually run your tests than if you had to build your code, launch an external tool, load your compiled module, then find and execute the test.  Less resistance to testing is a great thing. 

Regardless of which framework you choose, unit testing in the .NET world couldn't be easier.  Most frameworks make defining a test fixture and test cases very easy: simply create a class marked with a special test fixture attribute, then add methods marked with a special test attribute to create test cases.  In addition to a way to mark test cases, every unit testing framework I've ever seen includes an API for asserting various things: that two objects are equal/not-equal, that an exception was thrown, that one value is greater-than/less-than another, and lots more.  Using these assert methods, you can create tests that verify the behavior of classes, method, etc.

That's it for the introduction.  Hopefully you have a basic idea of what unit testing is and how you might use it.  In the next post, we'll delve in to NUnit and its API as we begin writing unit tests!

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About Matt

I am an overworked (and apparently overpaid) software developer with aspirations of acquiring a PhD in Computer Science. I started off coding in C over a decade ago.  Since then, I've migrated from C to C++ and branched out to C#, PHP, VB.NET, JavaScript, and worked with a wide assortment of other languages that I hope to never deal with again (I'm looking at you, COBOL). Oh, and yes, I've written some Java.  Does that make me a bad person?

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2009

Sign in