3

I need some advice to take the steps for TDD. How do we think in terms of interfaces for everything when we are writing code? Most of the testing books tell you how to write tests. But I want a book which tells whats the good way to write code in terms of TDD

2
  • 1
    "How do we think in terms of interfaces for everything when we are writing code?" - Remember, an interface doesn't have to be an interface.
    – user7043
    Nov 3, 2011 at 14:25
  • 2
    Write your code functionally; that is, write code that accepts values and returns values, with no state side-effects. Functional-style code is easier to write unit tests for. Nov 3, 2011 at 14:59

7 Answers 7

7
How do we think in terms of interfaces for everything when we are writing code?

YOU DON'T

Some of the other answers have touched on proper courses of design, but the unstated assumption in the question is subtle and flawed and deserves to be dragged kicking and screaming into the harsh light of day.

YOU WRITE THE TEST FIRST

It's common to read about TDD and think 'yeah that is a great idea' and then not do it. Instead, it's easy to fall back into code-first habits, and then wonder how you're supposed to think of all of the required and convenient interfaces as you're writing the code. Ya don't!

NO, SRSLY, WRITE THE TEST FIRST

Writing the test first forces you to invent the interface before you write the code. So you're not thinking about the code at all, you're thinking about the interface to test the code.

TDD IS NOT UNIT TESTING

TDD tests features, not units. Feel free to add unit testing and code coverage et al if you want, but all TDD requires is that every feature has automated tests. [But look into BDD for even easier test specification]

RED-GREEN-REFACTOR

This process will create the interfaces, get the code working, then refine the interfaces and the code, incrementally.

If you find during the refactoring step that, say, dependency injection (DI) would simplify the design, great, use it. But don't start with DI or any other tactic/technique as a preferential hammer. Let the code be your guide, and let the design evolve naturally, one test at a time.

1
  • 1
    +1 I agree with everything... except the oversized font. :P
    – Eric-Karl
    Nov 24, 2011 at 20:59
5

One of the biggest ways to make your code testable is to use Dependency Injection. This allows you to have a default implementation of the programming logic, but then when you go to test, you can mock that implementation in order to facilitate the tests.

public interface Model {
  //methods
}
public class DefaultModel : Model {
  //default implementation
}
public class Worker {
  private Model _model;
  public Worker(Model model) {
    _model = model;
  }

  public void DoSomething() {
    var result = _model.Action();
    //...
  }
}

//real code
Worker worker = new Worker(new DefaultModel());

//test code
public class TestModel : Model {
  //code for testing
}
Worker worker = new Worker(new TestModel());
//do testing of worker

Following this model, you can test Worker without needing the real implementation of DefaultModel and things it might depend on. You would then have separate tests to ensure DefaultModel is correct.

Here is a tech talk that is about writing testable code. I haven't watched it, but I've seen one given by the same guy. I just couldn't find a link for the one I watched.

2
  • 1
    +1 Refactoring stuff behind interfaces really does get you about 90% there
    – Doug T.
    Nov 3, 2011 at 15:13
  • While TDD often leads to use interfaces and DI-related ideas, it bothers me to see an answer which says nothing about writing tests first. Your code may end up looking that way, but this is a side-effect of TDD.
    – Mathias
    Nov 20, 2011 at 20:02
3

The principles of TDD are inherent in its name. The very first thing to keep in mind is, test first.

When you write a test that will exercise a new, previously unwritten object or method, you are basically defining the usage (translated: interface) for that new piece of code. That forces you to examine how you will want to use this code, which is critical to good design.

TDD also encourages a Single-Responsibility way of thinking. You need a new piece of functionality that will run every minute, get some data from the DB, perform some arbitrarily-complex calculation, then write the result back to the DB. Well, the first thing you need is code that will run every minute. So you create a Timer. Then, the timer needs to get something from the DB. That sounds like its own responsibility, so you mock out the use of some DB reader object. That forces you to think of how you want to use THAT object. This inherently-recursive system of defining the usage of something new before you define that something new generally leads to making decent, or at least easily-refactorable, design decisions.

As far as strictly thinking about interfaces, that's not really TDD, that's just "SOLID" design, specifically adherence to the "Dependency Inversion Principle" (stated succinctly, "classes should always depend upon abstractions, never upon other concrete classes"). If your class BusinessLogicObject requires code to write to the DB, it should never be dependent upon a concrete class DBWriter, but instead upon an IDBWriter. This promotes "loose coupling"; that way, if down the line you wanted to swap DBWriter for BetterDBWriter, then as long as BetterDBWriter implemented the same IDBWriter interface, you wouldn't have to change BusinessLogicClass to create or work with BetterDBWriter.

2

In my humble opinion you need several skills before you can become good at TDD.

First you need an understanding on how to build SOLID code, Agile Principles, Patterns, and Practices in C# (Robert C. Martin) is great for that. A short introduction is on DimeCast.

You also need Unit test knowledge

Than TDD knowledge:

And than you have read all the necessary knowledge and you need to make a mind shift. And the only way I know on how to do that is just by doing. Getting a coach would be great. Also to get a feeling for how the process might work you could watch James Shore's "Lets play TDD" 140+ screencasts.

0

Since you're on .NET you might want to watch DNRTV with the early episodes on TDD to get a good overview of that (and get a glimpse of the the MVC pattern).

JP Boodhoo goes through TDD quite clearly together with the concept of interfaces and dependency injection in those videos and at least helped me get a groove on how to do it.

0

The biggest point of interfaces in test code is the idea of "seams" used for "test isolation".

To unit test properly, you have to be able to isolate the code under test completely, so the only production code running is that under test. To do this you will have to replace other objects it depends on with fakes.

To do this quickly, mocking frameworks exist and they can quickly build fake objects that implement the interfaces you need, that way your code doesn't depend on the concrete type and a fake can be passed to the code as part of your test call.

Dependency Injection is to do with ensuring production code knows which concrete type to pass to your code.

-1

Read Clean Code by Robert C. Martin. You will learn how to write good code, and good code also happens to be easily testable.

Not the answer you're looking for? Browse other questions tagged or ask your own question.