Monday, December 5, 2011

Mocking part of the class under test

I'm sure that you all know that part of good unit testing includes extensive mocking of the dependencies of the piece of code under test. In order to do this succesfuly, you need to think about separation of concers as early as you can.
Therefore, all classes in are derived from an interface. When a class depends on another class, then it uses that class though the interface instead of directly. With moose, these interfaces are of course roles.

  1. The role
    1. use MooseX::Declare;
    3. role ICrawlerService {
    4.   requires 'Crawl';
    5.   requires 'CrawlSingle';
    6.   requires 'CrawlNew';
    7. }
    9. 1;

  2. The implementation
    1. use MooseX::Declare;
    2. use Service::ICrawlerService;
    4. class CrawlerService with ICrawlerService {
    6.   method Crawl(Int $startPage?) {
    7.     # do something
    8.   }
    10.   method CrawlSingle(Str $url!) {
    11.     # do something
    12.   }
    14.   method CrawlNew() {
    15.     # do something
    16.   }
    17. }
    19. 1;

  3. The depending class
    1. use MooseX::Declare;
    2. use Service::ICrawlerService;
    4. class CrawlerApp {
    6.   has CrawlerService => (does => 'ICrawlerService',
    7.                          is => 'ro',
    8.                          required => 1);
    9. }
    11. 1;

So now, CrawlerApp::CrawlerService will be set to CrawlerService in the actual application (either via dependency injection, or just manually). But when I'm testing, I can just replace that with a mock object that also implements the ICrawlerService role (I use Test::Mock::Class for this).

This is all really cool, but what if I want to mock out part of a class instead of an entire class.
Something like this:

  1. use MooseX::Declare;
  3. class MyClass {
  5.   method A() {
  6.     # do some stuff
  7.     my $var = $self->B();
  8.     # do some more stuff with $var
  9.   }
  11.   method B() {
  12.     # return something on which A() depends
  13.   }
  14. }
  16. 1;

Unit testing learns us that each unit of code must be tested separately. So if I want to test the A-method properly, I need to find a way to mock out the B-method.

As a side note: At work I program in C# and we use Rhino.Mocks as our mock library. The example above is something that cannot be done with Rhino.Mocks. This means that you need to make your own hand-crafted mock-implementation of the class under test and that you have to override the behaviour of the B-method in that derived class. This is a lot of work for a simple test and has frustrated me on numerous occasions! So you can imagine how overjoyed I was when I found out that perl does have way to do this:

Behold: Test::MockObject::Extends

This wonderfull module allows me to do this in my test code:
  1. my $cl = MyClass->new();
  2. $cl = Test::MockObject::Extends->new($cl);
  3. $cl->mock('B', sub { # Return something on which A() depends });
  5. # test the A() method. A will keep workin as before because our mock still returns the same value on which A depends
  7. $cl->called('B'); # check that B was indeed called by A

TADAAAAAAAAA: I can test the A-method without being annoyed by its dependency of the B-method! As a plus, I can even check to make sure that the A-method did in fact call the B-method.

I like it.


No comments:

Post a Comment