Pages

Showing posts with label unit-testing. Show all posts
Showing posts with label unit-testing. Show all posts

Tuesday, 12 January 2010

Setup, TearDown, When to Mock?

 

I’m writing this while eating at the Launceston Place, a very comfy restaurant near Gloucester Road. Warm decor, soft lighting and excellent service.

  • Setup and TearDown

Only use those to respectively initialise then destroy mock objects. Methods with [Setup] and [TearDown] attributes are called before and after each test so they’re the only methods that guaranty that things get cleaned up.

Best to avoid the [SetupFixture] attribute (to mark a class that contains Setup and Teardown methods called just once before ALL tests), as well as [TestFixtureSetup]/[TestFixtureTeardown] (called once only before all tests in a fixture). I tried to be clever and used them and ended up having to debug nasty side effects. Unit tests are supposed to run in isolation, so just one [Setup] before and one [TearDown] after each of them, that’s it.

  • What should be mocked?

Yesterday while writing a UT I bumped into this question: should I bother writing a mock for that object or can’t I just use the real thing?

One answer: objects that access external systems (DB, hard-drive, client API to some server-based system, …) should always be mocked (for unit-tests that is, not integration tests), otherwise the test is not repeatable, and it’s slow…

If a method under test accesses an injected object and this object does only in-memory stuff, then no need to mock it. It would make sense to mock it because after all this injected object is not actually the thing you want to test… Still, the more real objects are covered by the test, the better. As long as they all run in-memory and do not prevent the test from being repeatable, all is well. So no need to waste time writing a mock in that case.

Monday, 4 January 2010

Cheap IoC in native C++

In a 2008 episode of dnrTV James Kovac describes how to create a very simple IoC container. All the container does is mapping the name of an interface to an instance of this interface.

Easy to do in .NET with a generic Dictionary, but what about native C++? It turns out it's not that much more difficult: all you need is a STL map and C++'s typeid. 

What is an IoC container?
A class that given an interface ISomething returns the concrete object implementing ISomething.


Why do you need an IoC container ?

It's one way to do dependency injection.

Let's say you want to unit-test some business layer that relies on a data layer. The business layer sees the data layer through an interface (let's call it IDataLayer). The actual instance of IDataLayer is created by either:

  • the entry point of the code that uses the business layer (an executable for instance). This is production code and it has to create the object connecting to the real database.
 
int _tmain(int argc, _TCHAR* argv[])
{
    // Create the concrete DataLayer object
    DataLayer dataLayer;

    // Register it with the resolver
    Resolver::Register<IDataLayer>(dataLayer);

    BusinessStuff businessStuff;
    businessStuff.DoStuff();

    return 0;
}
  • the unit-test code. The IDataLayer object in that case is a fake object that doesn't connect to the real database and therefore makes unit-tests easily reproducible and fast.

void BusinessStuffTest::DoStuff_Nominal_DoesNotRaiseException()
{
    // Arrange

    // Create the concrete DataLayer object
    FakeDataLayer dataLayer;

    // Register it with the resolver
    Resolver::Register<IDataLayer>(dataLayer);

    BusinessStuff businessStuff;

    // Act
    businessStuff.DoStuff();

    // Assert
    dataLayer.AssertStuffWasDone();

}

    Implementation of the Resolver class
    The Resolver class has two methods Register and Resolve. Here is the code for the Resolver class (very straightforward):


      #pragma once

      #include
      #include

      using namespace std;

      ///
      /// Allows you to Register and Resolve global objects
      ///
      class Resolver
      {
          static map<string, void* > typeInstanceMap;

      public:

          template<class T> static void Register(const T& object)
          {
              typeInstanceMap[typeid(T).name()] = (void*)&object;
          }

          template<class T> static T& Resolve()
          {
              return *((T*)typeInstanceMap[typeid(T).name()]);
          }
      };

      This is a just a starting point. In production Resolver could do with methods such as Unregister(), IsRegistered(), UnregisterAll()...


      An IoC container is one way to do dependency injection. Other ways include:
      • pass the injected object using the constructor
      • pass the injected object using a setter method
      Both methods above can become tedious very quickly in a big project.
      I tried all 3 methods and I find the IoC approach scales rather well. I've used it for 10 months in a production project and it works nicely with unit tests.

      Resources:

      Jean-Paul Boodhoo on the gateway pattern
      Roll your own IoC container (dnrTV)
      The Art of Unit-Testing where Roy Osherove tells everything about dependency injection and fake objects.

      Sunday, 28 December 2008

      Using NUnit with C++ - Part 2

      Follow up from part 1 We've been experimenting at work with writing unit-tests against native code for 2 months now. We're using NUnit together with CruiseControl and it works fine.
      • the test project is a C++/CLI DLL compiled with /CLR option.
      • the project containing the code under test has an additional project configuration that generates a static LIB instead of an executable.
      • the test project links to the native LIB above.
      What if the code under test is C++ compiled with /CLR? For some reason I thought it wasn't possible to create a static LIB when the /CLR option was active. C++/CLI is very flexible and you can generate a static library containing a mix of managed and unmanaged code. The purpose of building a mixed static lib is to easily import native code into a test project. We keep the test project and the code under test in separate solutions.