Inversion of Control is NOT about testability

Many times, I've heard that the best thing about IoC is that it enables testability.  When dependencies are injected into a class, it's easy to test that class.  Testability is a side-effect of good design.  The lack of testability signifies a bad design, but the enable to test a class does not, by itself, signify a good design.  The causality is one-way.

Let's take a look at an example:

 

public class MissileLauncher{
    private INavigator _navigator;
    public MissileLauncher(){
        _navigator = new MissileNavigator();
    }
    public void Launch(){
        //get direction guidance from _navigator and launch a missile
    }
}

 

This can be a good design or bad design depending on your needs.  We are coupling to an interface, INavigator, but we are also coupling to a specific implementation class, MissileNavigator.

If MissileLauncher can't do its job without MissileNavigator, then this coupling makes sense, and the class should be tested together with MissileNavigator.  Other examples are operations on domain objects:  The logic makes no sense without the Customer object, specifically, so the operation should be tested with the real Customer, and not a mock object.

If, however, MissileLauncher is a concept orthogonal to MissileNavigator, then INavigator adequately illustrates the necessary interaction.  In this case, coupling to MissileNavigator is detrimental to our design if the MissileLauncher only depends on the operation exposed by INavigator.

We would change this class to the following:

public class MissileLauncher{
    private INavigator _navigator;
    public MissileLauncher(INavigator navigator){
        _navigator = navigator;
    }
    public void Launch(){
        //get direction guidance from _navigator and launch a missile
    }
}

Here, we have inverted the control over locating or creating the dependency.  Now MissileLauncher does not care about the actual class, just that it receives an instance of the interface.  With the interface it can do its job.  This is a better design if you have determined MissileLauncher's behavior is orthogonal to the actual MissleNavigator class.  Again, you have to make many hard decisions about design.  Do you couple. . . or not. 

In order to do anything useful in software, you must couple.  You must couple to something. 

The coupling determines how you will test.  If you couple to a concrete class, you will test the code together with that class.  If you only couple to an interface, you can test the class with only the interface.

Testability is a by-product of inversion of control, and design is the driving factor to invert the control or retain it.  Don't invert all control just so each class can be tested independently of all other classes.  You must decide where coupling is appropriate and where it is not.


Trackbacks

Inversion of Control happens to… at Rinat Abdullin Posted on 8.14.2008 at 5:46 PM

Pingback from Inversion of Control happens to… at Rinat Abdullin

Reflective Perspective - Chris Alcock » The Morning Brew #159 Posted on 8.15.2008 at 2:19 AM

Pingback from Reflective Perspective - Chris Alcock » The Morning Brew #159

Dew Drop - August 15, 2008 | Alvin Ashcraft's Morning Dew Posted on 8.15.2008 at 8:42 AM

Pingback from Dew Drop - August 15, 2008 | Alvin Ashcraft's Morning Dew

Weekly Web Nuggets #25 Posted on 8.15.2008 at 2:30 PM

General The Tortoise And The Hare : Tim Barcz has a must-read post comparing enterprise development to the fable of the tortoise and the hare. As someone who's been dealing with this sort of cycle for the last few years, I couldn't agree more. Diligent

Arjan`s World » LINKBLOG for August 15, 2008 Posted on 8.15.2008 at 3:13 PM

Pingback from Arjan`s World » LINKBLOG for August 15, 2008

Testability in .Net Posted on 8.15.2008 at 4:58 PM

Testability in .Net

Testability in .Net Posted on 8.15.2008 at 5:02 PM

Testability in .Net

Inversion of Control is NOT about testability Posted on 8.16.2008 at 10:41 AM

Inversion of Control is NOT about testability

The Path to Zen » Blog Archive » But Then There Is Also Unnecessary Coupling Posted on 8.18.2008 at 9:05 PM

Pingback from The Path to Zen » Blog Archive » But Then There Is Also Unnecessary Coupling

On good design and defining success Posted on 8.19.2008 at 8:27 PM

This is for the most part a reaction to conversations on design: Testability in .Net Design and Testability

Comments

Peter Ritchie said on 8.14.2008 at 9:02 AM

Indeed. If IoC and Dependancy Inversion is just for testability, the point has been missed.

Testability is often used as an introduction to reduced dependancies, then isn't followed up with sound design principles for the rest of the design.

Bjorn Reppen said on 8.14.2008 at 9:27 AM

You can be my wingman any time!

Matt Baker said on 8.14.2008 at 9:54 AM

You are totally right. I've been guilty of this in the past where the desired end case is testable code, and Isolation via IoC is the simplest solution. The problem is that the desired end case was testable code, not good overall design. Like you said, testability is a side-effect of good design, but testability doesn't necessarily signal good design.

Justin Etheredge said on 8.14.2008 at 10:27 AM

I know I am probably arguing semantics here, but I think that what you describe as "not coupled" is better described as loose coupling.

One class implements an interface, and the other uses that interface. If one side needs to change, then the interface might change, which means that the other side has to change. I would describe that as "loosely" coupled and therefore I think this is why the term is so often used.

Will Shaver said on 8.14.2008 at 10:38 AM

Ok, this is totally picky... but you're the kind of person who wants to be perfect right?

You want by-product not bi-product.

en.wikipedia.org/.../Byproduct <-- Extra Product

en.wikipedia.org/.../Biproduct <-- Crazy Math Stuff

Scott Bellware said on 8.14.2008 at 10:40 AM

Well, no, there is indeed such a thing as loose coupling. Maybe it's not well understood, but i don't think you've offered much more here than a grab at a new naming and term coinage opportunity.

To wit, 'testability' is a design quality, and it presumes loose coupling and inversion of control. So it is perfectly reasonable to say that inversion of control is about testability because 'testability' as a part of the design quality pattern language encompasses all of the benefits you've laid out here.

'Testability' doesn't necessarily mean that you have tested something, or even that you will test it. It means that the design expresses the qualities that make software amenable to being tested more easily.

Bryan Reynolds said on 8.14.2008 at 10:59 AM

Its not the end of the world to IoC for testing. I agree that your code should be doing what it is meant to do. If it is suppose to be coupled, then couple it or vice versa.

Peter Ritchie said on 8.14.2008 at 11:01 AM

Semantics of coupling is a rat hole. Clearly one piece of code uses another piece of code. That can be behind one or more levels of abstraction and thus isn't directly dependant. But, for the application to run (even if coupling is defined at runtime) there is some for of coupling.

I view "loosly coupled" as two components that are not compile-time coupled.

Daniel Jin said on 8.14.2008 at 12:07 PM

I understand what you are saying. But obviously there are exceptions to that. What about patterns like the different variations of MVP. That seems to be me like a case of IoC being applied for the sole purpose of testing. Rarely do we have the need to use the same presenter to drive multiple views. But we do it because we want to test. Which in fact actually demonstrates your point perfectly, because I see MVP as a bad use of IoC for the exact reason that you are stating.

I think another point to make is that you shouldn't decouple (loose-couple) everything throughout the system just so you can test the interaction between all objects. Some interactions are meant to be implementation details. If your tests become too granular, you run the risk of invalidating brittle tests the minute your implementation changes. Then TDD becomes a drag and drain on your resources.

Chris Holmes said on 8.14.2008 at 1:56 PM

I think Scott has the right take on this from the testability standpoint.

But Jeff's point comes at this from the other end of the tunnel, and I think it's just as valid of a point: IoC is a nice tool and can be very useful without testing.

IoC, as a useful design tool, can exist independent of testability.

Jeremy Gray said on 8.14.2008 at 3:49 PM

@Daniel - We also do MVP because we want SoC and enforcement of SRP, which are good qualities in and of themselves. Testability is a bonus. A tasty, tasty bonus, but just a bonus nonetheless.

Also, test granularity runs orthogonal to test brittleness, so if you are seeing highly granular tests becoming brittle then I would suggest you start looking at things other than granularity. (as a note, in my personal experience test brittleness has most often been an indirect indicator of SoC and SRP violations.)

Jeffrey Palermo said on 8.14.2008 at 4:25 PM

@Justin,

I won't spend a lot of time arguing that point since it was a side-note to the point of the post. I'm fine with "loose coupling", but users of interfaces are not coupled to implementers of interfaces. In fact, those users don't even know if there are classes that implement the interface.

But, yes, if the interface changes, all those coupled to the interface must change.

Jeffrey Palermo said on 8.14.2008 at 4:26 PM

@Will,

Thanks. I've changed that.

Jeffrey Palermo said on 8.14.2008 at 4:31 PM

@Scott,

I agree about testability being a design quality.

You don't have to focus on testability to use Inversion of Control. In fact, there are other ways to make code testable without IoC. IoC is independent of the testability design quality. Testability depends on decoupling techniques, and IoC is just one of those techniques.

Jeffrey Palermo said on 8.14.2008 at 4:34 PM

@Daniel,

I used to do MVP with WebForms, but it was more about simplifying the presentation code. Again, testability was a side-effect. Code-Behind is so complicated to understand that by separating controller concerns into a separate class, the code was simpler, and the infrastructure (WebForm) was at arms length.

I'll acknowledge that some people do MVP just for testability, but since testability depends on Separation of Concerns, they have to achieve SoC first.

Jeffrey Palermo said on 8.14.2008 at 4:35 PM

@Jeremy,

Re: brittle tests.

There are SO many things that can cause brittle tests.

Sean Kearon said on 8.14.2008 at 6:16 PM

I completely agree - IoC is not all about testability; that indeed is a side effect. However, I cannot agree that there is not such thing as loose coupling and think it is not helpful to state so. Because an argument can be stated does not mean all arguments are semantics!

In your first example, you are not coupling to an interface as the interface is private - it is encapsulated. Your second code example, where you allow the client to specify the navigator is precisely an example of loose coupling - the dependency is as weak as is feasible.

Minimising coupling is a practical design principle that can significantly aid system flexibility and maintainability. As such it should receive due credibility!

Jeremy Gray said on 8.14.2008 at 8:16 PM

@Jeffrey (re: there being so many things that can cause brittle tests) - Exactly!

Damon Wilder Carr said on 8.14.2008 at 11:30 PM

Jeffrey,

I was wondering why you didn't use design patterns to drive your ideas home here. Indeed they are at the very core of the message your communicate.

It's a real problem would you not agree that far too many .NET developers believe fluency in code is the driver exponentially over fluency in OO Design (to this posts quite good point)?

Why not say 'strategy pattern' at some point'? And then perhaps the critical idea for every .NET developer is a mastery of the WHY behind 'favor encapsulation over inheritance'. Some know the words, fewer the defining reasons for saying it, fewer still the complete ramifications of doing it. And this has been said since when, 1985? Earlier? Since early SmallTalk...

So here is my message: learn OO design. Master it. It will change your life.

There I said it. My job these days is 90% teacher, 10% execution. I want to reverse that but cannot until teams allow that to happen!

So I'll offer what I hope one person take me up on (again many here will groan as they read the book on release).

This however is in my opinion a masterpiece by a grand master (Uncle Bob Martin). And for the C# developer! SO SO rare in OO design....

The book is NO AN AGILE BOOK, it's an OO Design book which comes BEFORE Agile I assert (as does Bob without being so explicit). People think Agile is code. It is... Assuming your team can speak in patterns or you are way too slow. And if design at basic levels is not driving the code, then almost certainly not building for maintenance, your building for release one, so you can move to other problems.

The test of an agile test is if they are building for release 1.1, and indeed 2.0 from very early in my world anyway. Most are like sprinters attempting a marathon instead..

Agile Software Development, Principles, Patterns, and Practices (Alan Apt Series) (Hardcover)

www.amazon.com/.../0135974445

Sure read that Linq book but you will gain likely more if lacking this knowledge from the above.

Kind Regards.

Damon Wilder Carr http://blog.domaindotnet.com

Daniel Jin said on 8.15.2008 at 8:13 AM

@Jeffrey & Jeremy

Sure, SoC is good concept and all. But I'm just speaking from my experience of using the pattern. You have this tightly coupled View and Presenter classes, where the View has to call the Presenter directly to do half of the useful work, and the Presenter has to call the View directly to do the other half of the useful work. I often end up with a very thin View codebehind where nearly every single line of code is just calling out to the Presenter, and makes me wonder, what's the real value in supporting this structure?

J.P. Hamilton said on 8.15.2008 at 9:45 AM

SoC??? SRP??? To me inversion of control is all about facilitating DIP.

Rogério Liesenfeld said on 8.16.2008 at 5:09 PM

In reality, "testability" may not be a design issue at all. It only is if you assume that certain program designs can't be tested in isolation within the confines of the language and the runtime (the JVM in our case). But what if you could isolate a class from any of its dependencies, even if they consist of static method calls or objects directly instantiated with "new"? In fact, this has already been done in Java.

Haacked said on 8.18.2008 at 12:18 AM

Regarding coupling, realize that accessing another class via its interface doesn't guarantee loose coupling. Coupling has to do with the extent one class/module relies on another. For example, you may have class A access class B via interface C, but if A and B also both access the same external resource, say a database record, then they may well be tightly coupled, even though A and B don't have a direct reference to each other.

Perhaps you meant that a class either has or doesn't have a direct reference to another class. Reference != coupling.

Jeffrey Palermo said on 8.18.2008 at 7:01 AM

I removed the part about no loose coupling. It was an over-analytical aside that distracted from the main point of the post.