TDD and FP 9

Posted by Uncle Bob Sat, 08 Aug 2009 21:31:10 GMT

No, this isn’t a blog about how to do TDD in a functional language. Rather, it’s a blog about the similarities between the two disciplines.

First, a quick definition. Functional Programming is programming with functions. In particular, functions that have no side effects. A function that has no side effects is a function that returns the same value given the same inputs irrespective of the state of any other part of the system.

Does this mean that functions in a functional program are truly free of all side-effects? Clearly not, since they are usually running on machines where the machine instructions are all about side effects. It’s just that all the side effects have been cancelled out by the time the a true function returns. The function leaves no residual state behind.

A unit test, properly written in TDD style, has no side effects. Every time you run it, it will give you the same (hopefully green) result. You can run as many tests as you like, in any order you like, but they’ll always give you the same result. Why? Because they leave no state behind. They have no side-effects.

It’s not that the operations executed by the unit tests don’t have side effects. Most of the time they do, since most of the time we are writing them in languages (like Java) that are rife with side effects. But that’s not relevant since all those side effects get cancelled out by the time the unit test completes.

Does the function you are testing create files? That’s ok, your test will delete them before it completes. Does the function you are testing create rows in a database? That’s OK, your tests will remove them before it finishes. Does the function you are testing change any state at all? If so, the unit test will put that state back where it was before the test was run. Unit tests are atomic operations that leave the state of the system unchanged!

In short, code written with TDD is code that has been assembled into pairs that have no side effects. One side of the pair is a unit test, and the other side of the pair is production code. Together they are functional, even if separate they are not.

Thus, TDD is Functional Programming (of a kind). If your unit tests are very granular, you are more likely to write your production code in a functional style; or at least in a style in which side-effects are so localized that they can be managed within the scope of a single test.

Certainly TDD is not a replacement for learning a functional language. I think there’s much to be gained by learning Haskell, or Scala, or Clojure. However, TDD is a style of programming that encourages programmers to severely limit side-effects and keep them manageable within very small scopes.

Comments

Leave a response

  1. Avatar
    http://uglylispcode.wordpress.com about 1 hour later:

    I would go even further and state that TDD is a declarative language. There is a major side-effect generated from TDD that I think you missed.

    Well designed and easily maintainable source code.

    ;)

  2. Avatar
    Samuel A. Falvo II about 10 hours later:

    Do your tests rely on files? Then they’re not unit tests.

    Do your tests rely on databases? Then they’re not unit tests.

    Instead of relying on files and databases, you should use dependency injection to “mock” or “stub” (as appropriate) the relevant resources. THIS, then, is genuinely controlling state, and putting an increasing emphasis of side-effect-free behavior on the implementation code, where it belongs.

    The test should NEVER affect whether a piece of software is truly side-effect free, except in-so-far as the principles of TDD lead directly to highly modular and re-usable code. Indeed, the whole POINT of the test is to verify the lack of unintended side-effects of calling some code-under-test.

    Saying prod-code plus TDD equals FP is grossly misleading. You should always be writing prod-code functionally. As Djikstra and Hoare both expressed at one point in each of their lives, a programmer should be able to mathematically prove the soundness of their code at any point in time. Unit tests, while no substitute for formal verification, at least brings the coder in the right direction.

  3. Avatar
    Thomas Eyde about 15 hours later:

    @Samuel,

    Your’re right, depending on which school you belong to. Some prefer to split their tests in well defined categories, as unit tests, integration tests and so on.

    I don’t do that anymore. I don’t even know the names nor definitions of all the different tests. Why?

    Two reasons: First, I always forget to implement some of the mocks I introduce. Second, my tests are my design tool, and I really don’t like to call them tests.

    I use mocks when the real implementation has no meaning to my specifications, is to slow, or is to hard to set up.

    In my recent project, however, I have split my tests in two: All database interactions goes into the integration tests. That’s because our CI-build server has no access to a database server.

  4. Avatar
    Monis Iqbal about 20 hours later:

    I don’t agree to the statement: “Thus, TDD is Functional Programming (of a kind).”

    Yes, the outcome of both can be the same but the way they are “programmed” is very different and FP is a different “paradigm” altogether.

  5. Avatar
    Jason Y 3 days later:

    Thanks for the post! This is an interesting observation.

  6. Avatar
    Don Stewart 10 days later:

    TDD is purely functional programming, by this argument. As Haskell is by default, and other function-oriented languages are by convention.

  7. Avatar
    Greg Buchholz 10 days later:

    If you think TDD is like FP, just wait until you try prolog, where you can write things that look like tests, but they are actually your whole program.

  8. Avatar
    han 8 months later:

    I don’t even know the names nor definitions of all the different tests. Why?

  9. Avatar
    supplynflshop about 1 year later:

    good

Comments