Law of Demeter is easy to spot when you need extra mocks

In code, the Law of Demeter (aka the one-dot rule) is a principle that basically states:

void DoSomething(IFoo foo)
{
    foo.GetStatus(); // good
}

void DoSomething(IFoo foo)
{
    foo.Profile.GetStatus(); // bad
}

In this example, DoSomething() knows intimate details about what an IFoo’s insides look like. This is bad because it’s additional coupling that will bog us down later — if we ever want to change the internal composition of IFoo, we will have to also update all pervert methods like DoSomething() that depend on it.

In some situations, this rule really doesn’t matter, e.g. for trivial or built-in types (datasets come to mind). But other times we definitely want to avoid it. One trick I have discovered for identifying trouble spots is a code smell you might encounter when isolating something for unit testing:

var foo = new Mock<IFoo>();
var profile = new Mock<IProfile>();
profile.Setup(p => p.GetStatus()).Returns(/* the thing we are testing */);
foo.SetupGet(f => f.Profile).Returns(profile);

DoSomething(foo.Object); // assert etc 

Does this code look familiar? In it we are setting up two mocks:

  • The Profile instance, which has a method we want to stub out and verify
  • The parent IFoo, which only exists to return the child Profile

The code smell is all the extra setup cruft required — two levels of nested mocks for just one parameter we are testing. If we do move method and provide a GetStatus() method on IFoo (that internally delegates to Profile), our test immediately becomes a lot clearer:

var foo = new Mock<IFoo>();
foo.Setup(p => p.GetStatus()).Returns(/* the thing we are testing */);

DoSomething(foo.Object); // assert etc 
August 26, 2009

4 Comments

Bengt Berge on August 28, 2009 at 7:05 am.

Good points about the Law of demeter. However when using Moq you don’t need the extra mocks. Moq has a cool feature called recursive mocks. It looks something like this:

var foo = new Mock();
foo.Setup(f => f.Profile.GetStatus()).Returns(SOME_RETURN_VALUE);

I just wrote about this (as a comment to your post) on my blog.

Richard on August 28, 2009 at 9:20 am.

Ben,

Shame on me for not knowing Moq could do that! Thanks for the tip!

Leave a Reply