Testability vs coupling

I want to describe a situation I came across where designing for testability seems to be in conflict with the always-valuable principle of loose coupling.

Summary of coupling

Steve McConnell in Code Complete describes coupling as follows.

Coupling describes how tightly a class or routine is related to other classes or routines. The goal is to create classes and routines with small, direct, visible, and flexible relations to other classes and routines, which is known as “loose coupling”.

McConnell furthermore lists a number of different types of coupling, some of which are better than other.

  • Simple-data-parameter coupling: All data passed between A and B are of primitive data types and passed through parameter lists. This kind of coupling is normal and acceptable.
  • Simple-object coupling: A instantiates an object of type B. This kind is also fine.
  • Object-parameter coupling: A requires B to send it an object of type C. This type is tighter since it requires B to know of C.
  • Semantic coupling: A makes use of, not some syntactic element of B, but knowledge of its inner workings. Should be avoided.

Dependency injection to simplify testing

So, loose coupling is definitely good as it makes the system easier to work with. However, when I read one of Jeremy Miller's excellent posts I started thinking. He suggests using dependency injection to simplify testing in some cases, which to me seems to be creating tighter coupling within the system.

More specifically, the problem he describes is as follows. A class ClassUnderTest internally creates an object of type Dependency. However, if it is hard to create a new instance of Dependency equal to the first one (e.g. a datetime object representing “now”), ClassUnderTest becomes very difficult to test.

When the unit test wants to create a copy of the the hard-to-reproduce-object Dependency to compare with ClassUnderTest‘s Dependency instance, a non-equal instance is created (the system clock has changed) and the test fails.

The solution is to provide the object under test with either:

  • the Dependency object in question itself, or
  • an object which provides ClassUnderTest with the wanted Dependency instance.

I agree with this completely. In fact, I’ve had the problem a number of times myself, and solved it successfully this very way.

In terms of coupling

With respect to coupling, the case is that before we add the test (let’s assume we’re testing legacy code) we have simple-object coupling, since ClassUnderTest creates another object Dependency. However, when we test ClassUnderTest we want to create another object of the same type as Dependency equal to the one ClassUnderTest created.

As noted above, the solution is dependency injection — to provide ClassUnderTest with Dependency, either directly or indirectly. That way, we have the possibility to get hold of Dependency to perform the assertion in question. In terms of coupling however, this changes the coupling to be of type object-parameter coupling which is considered tighter than the type of coupling we had before.

A counter argument

Thinking about this, I thought that maybe the key here is that to test ClassUnderTest in the first case, the test would need to be semantically coupled to ClassUnderTest because it would have to know what kind of Dependency object it created internally (“now” vs any other datetime) and try to recreate it in some clever way. So we actually replace semantic coupling with object-parameter coupling — an improvement.

The counter argument (to the counter argument) is that without the test code, we would have no need to create such an object and there would be no semantic coupling in the first place. Thus, writing the test tightens the coupling in the system.

The question

Am I missing anything, or is this an example of where designing for testability actually leads to worse (as in more complex and more tightly coupled) design? Or am I just getting lost completely in theory? Maybe this slightly tighter coupling is nothing compared to what we gain from having a good and simple unit test in place.

2 Responses

  1. Great post!

    However, to test ClassUnderTest in isolation from an internally instantiated Dependency, it’s not necessary to “create another object of the same type as Dependency”. You can instead simply mock the Dependency class, allowing the test to specify the behavior that ClassUnderTest will see from that internal Dependency instance.

    So, the (unit) test for ClassUnderTest will only need to reflect the simple-object and simple-data-parameter forms of coupling on Dependency, not any semantic coupling. That is, the unit test will have no knowledge about the “inner workings” of the Dependency.

    Rogério Liesenfeld - March 2nd, 2011 at 12:34
  2. Thank you for your comment Rogério!

    I agree that having a unit test creates no real problem with regards to coupling. Providing a dependency really just makes the existing “hidden” coupling more visible.

    But what do you do when you mock if not “create another object of the same type as Dependency”? The object/type is created at runtime, of course, but still, it is precisely what you do. :-)

    Henrik Jernevad - March 2nd, 2011 at 14:20

Leave a Reply