"Too Deep" Unit Tests

Thursday, December 29th 2011

Yesterday, I ran across a unit test at my work that had fourteen lines of “arrangement”, one line of “action”, and ten lines of “assertions.” The unit test literally had more lines than the method it tested!

Yikes.

The author of that unit test should have figured out at that point that they had ended up with some bad implementation. To that end, I have a small list of indicators brought to light by unit tests that I use to help me understand if I have a poor implementation.

I identified these indicators because I have written code sometime in my past that I would now find offensive because of the bad design it implied. Unit tests that have any of these flavors can imply something wrong with your implementation.

This one, the “too deep of an arrangement” indicator allows us to realize that we’ve inadvertantly broken the Law of Demeter.

If, in your test, you have a mock that returns a mock that returns a mock (or stubs), then your class knows too much about its collaborrators. Your class has asked questions of its collaborators rather than telling them to do something.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Test]
public void DeepArrangement() {
// Arrange
var iDoSomething = MockRepository.GenerateMock<IDoSomething>();
var iLikeYou = MockRepository.GenerateMock<ILikeYou>();
var iRobot = MockRepository.GenerateMock<IRobot>();
iDoSomething.Stub(m => m.DoYouLikeMe()).Return(iLikeYou);
iLikeYou.Stub(m => m.WhatAreYou()).Return(iRobot);

// Act
var o = new InjectedClass(iDoSomething);
o.DoSomethingWithDependency();

// Assert
iRobot.AssertCalled(m => m.BeepBeepBoopBoop());
}

To fix this problem, you should have the class under test tell its collaborator what to do rather than ask for an intermediate result.

1
2
3
4
5
6
7
8
9
10
11
12
[Test]
public void ShallowArrangement() {
// Arrange
var iDoSomething = MockRepository.GenerateMock<IDoSomething>();

// Act
var o = new InjectedClass(iDoSomething);
o.DoSomethingWithDependency();

// Assert
iDoSomething.AssertCalled(m => m.ThenDoIt());
}