NDC2010: Michael Feathers – The deep synergy between good design and testability

In this session Michael Feathers basically killed the argument based on; I don’t want tests to drive my design with a simple example base on the code smell; Iceberg class.

IMAG0110

Private methods are really hard to test, and here we have a class with a bunch of them, It will be hard to test, but in addition to that it’s also a bad design decision. It violates the single responsibility principle.

A better design would be to break this out in smaller classes, something like;

IMAG0111 Which honors SRP and will be much easier to test.

So why is this true?
There is basically two reasons, related to each other, that drives this synergy. To begin with you are consuming and using the api, which quickly lets you feel the pain. The second reason is based on the fact that test help you understand code. Badly designed code will be hard to test since it’s also very hard to understand what it does. In the light of this, SOLID principles makes a lot of sense. Not only does it allow you to test your code, but it makes it easier to understand.

With these facts, Michael stated a simple truth;

Solving Design Problems
Solves Test Problems

It’s not about letting a test tell you what to do, making code testable does not necessarily imply better design. But better design will make your code easier to test.

This is also why he has problems with “Groping test tools”, tools that let you test private parts. He feels they are a “Get out of jail free card”. Which removes an important force to make the design better; Pain. Pain is useful as a learning tool in that it tells you what to avoid. Sure removing test-pain will make the code easier to test, but will it make it easier to maintain or extend? Usually not.

He then concluded that testing isn’t hard, testing is easy in the presence of good design.

Reflections
This was a great talk! By examining relationship between test-pains, code smells and design principles, Michael made a clear point that there is a synergy. If you find it hard to test your code, there is usually some other problem with it.

He didn’t want to impose TDD on everyone, but he made his case that test will help you understand your code better and act as a constraint that forces you into good design. Something you should be doing anyway.

 

Catalogue of testing pains and relation to code smells

State hidden within a method – you find yourself wishing you could access local Variables in a test. Methods are typically to long and are violating Single Responsibility Principle.
Difficult Setup – Instantiating a class involves instantiating 12 others. This pain says something about the design, it means that the class isn’t factored into smaller pieces. Problem: To Much coupling.
Incomplete shutdown – pieces of your code lose resources and you never realize it until you test. Classes don’t take care of themselves, poor encapsulation.
State-leaks across tests – The state of one test affects another. Smells of improper use of sIngletons or other forms of global mutable state.
Framework Frustration – Testing in the presence of a framework is hard Insufficient Domain separation. Lacking good separation of concerns.
Difficult mocking – You find yourself writing mocks for objects returned by other objects Law of Demeter Violations
Difficult mocking 2 – hard to mock particular classes Insufficient abstraction and / or dependency inversion violation.
Hidden effects – you can’t test a class because you have no acess to the ultimate effect ot its execution Insufficient separation of concerns and encapsulation violation
Hidden inputs – There is no way to instrument the setup conditions for a test through the API Over encapsulation, insufficient separation of concerns
Unwieldy parameter lists – it’s too much work to call a method or instantiate a class To many responsibilities in a class or a method. SRP violation
Insufficient access – you find yourself wishing you could test a private method. To many responsibilities in a class. SRP violation
Test Trash – many unit tests change whenever you change your code. Open/Closed violations

NDC2010: Michael Feathers – Testable C#

IMAG0104 This session was basically explaining a simple rule that regardless if you are using TDD; should be followed at all times:

Never hide a TUF in a TUC

What did Michael mean by that?

There are two things that can be “Tests Unfriendly”, features and constructs. The common denominator of the two is that they are hard to replace or isolate and thus impose a lot of constraints on what need to be up and running to test.

He then went on to explain exactly what TUF’s and TUC’s to look out for.

Test Unfriendly Features (TUF)
A TUF is something that has a tight coupling to a resource that’s hard to mock, fake or replace for testability. He listed a few;

  • File access
  • Long computations
  • Network access
  • Database access
  • Global variable manipulation
  • Use of 3rd party libraries or frameworks

The list is in no way complete but clearly shows a pattern and really follows the ideas that a unit test should be isolated from any external dependencies.

Test Unfriendly Constructs (TUC)
A TUC is a language feature that makes it hard to fake, mock or replace a piece of code. He was very clear that he didn’t say that these features where bad in any way, just that by using them we should be aware that we might introduce testability issues. Certainly in combination with TUF’s.

He listed a few C# constructs that wasn’t replaceable (or at least hard to replace without stuff like TypeMock);

  • Private/static/sealed/non-virtual methods
  • Object constructors
  • Static constructors
  • Static initialization expressions
  • Sealed classes
  • Static classes
  • Const/read only initalization expressions

    When dealing with these TUC’s, since they aren’t completely worthless, he gave a simple advice; “Do not code with the idea “why would anyone ever want to replace this” but think instead in terms of “this is really important that nobody can change or easily access”.

Reflections
This was a good talk, not in terms of new things I didn’t know. But in introducing rules and arguments for building testable code. Not everyone will write code TDD-style but by at least making them following the simple rule of “Don’t hide a TUF in a TUC”  it will be easy to add tests later on if there is something you are curious about or want to validate.

This was a great take away that I see I can put into use right away.

Tip: Mocking callbacks using RhinoMocks when lambdas are involved

There is several cool language features available in C# 3.0, one of them is lambdas. It’s a handy tool when you want to execute a few lines of code where a delegate is expected. Fredrik Normén and I chatted about one of those places today, callbacks in async scenarios.

Given this callback:

public string ResultOfServiceCall { get; private set; }
public ContextThatDoesStuff(IServiceWithCallback service)
{
    service.Begin(
            () => ResultOfServiceCall = service.End()
            );
}

How would you set up a mock to handle that? Somehow you need to intercept that lambda and execute it. One way, there might be others, to solve this with RhinoMocks is to use the WhenCalling feature. A simple test to demonstrate:

 

[TestMethod]
public void TheTest()
{
    var mock = MockRepository.GenerateMock<IServiceWithCallback>();

    mock.Expect(service => service.Begin(null))
        .IgnoreArguments()
        .WhenCalled(invocation => ((Action)invocation.Arguments[0]).Invoke());

    mock.Expect(service => service.End())
        .Return("The String");

    var context = new ContextThatDoesStuff(mock);


    Assert.AreEqual(context.ResultOfServiceCall, "The String");
}

In the above example we capture the in parameter, here it’s a lambda, casting it to an Action delegate and invoke it.

I just love the flexibility in RhinoMocks, don’t you?