A chronicle of my journey on the road of professionalism and craftsmanship in software engineering.

Friday, June 7, 2013

Delayed answer to interview question

I recently interviewed with somebody who asked me, "how could you do tdd (test driven development) without mocks?", referring to a mocking framework of some kind. I mumbled something about our tdd process being immature (true), and some of our unit tests really being integration tests (also true), but the more I think about it, the more I think he had fallen into a trap I see all the time. For example,

public class ClassA

{

private ClassB b;

public ClassA(ClassB b)

{

this.b = b;

}

public void Method1()

{

//logic that operates on data in this.b

}

public void Method2()

{

//some code here

b.SomeMethod();

}

}

Now, ClassA.Method1 is perfectly testable as is - no need for mocking frameworks, IoC Containers, service locators, etc. Just var b = new ClassB(), set up the data, and test away - like this:

[Test]

public void TestMethod1()

{

//arrange

var b = new ClassB();

b.SomeProperties = SomeData;

//act

var a = new ClassA(b);

a.Method1();

//assert

Assert appropriate state change.

}

[Test]

public void TestMethod2()

{

//Requires creating an IClassB interface and refactoring ClassA to take an //IClassB

var mockB = YourFavoriteMockingFramework.GetMock();

//set up mock object

var a = new ClassA(mockB);

a.Method2();

//assert

Assert that mockB.SomeMethod() was called.

}

I would argue that not only is TestMethod1() a better (more valuable) test than TestMethod2(), but ClassA.Method1() is better code than ClassA.Method2(). Why? Well, the whole idea of object-oriented programming is that the data and the methods that operate on that data live together in an object. It's entirely possible that the call to ClassB.SomeMethod() belongs somewhere else.

There will certainly be classes whose collaborators will be more involved than the example given, but to the extent you can keep logic in classes that depend on data and leaf objects, your system will be simpler, easier to test, and you may even be able to say, "Yes, we did tdd without a mocking framework".

*Notice that I didn't even use an interface for the dependency injection. I also think there's an over-reliance on IoC Containers, but that's another blog post.