Many people are dabbling in functional programming these days. Haskell is more popular than it ever has been, OCaml has it's adherents, and we are starting to see common functional idioms spread throughout the industry.
Why am I not happy?
Here's why. I think that most functional programming languages are fundamentally broken with respect to the software lifecycle. I realize that's a bold statement. Let me back it up.
Imagine these lines of code in any object-oriented language (I'll use Java here because it is familiar to most people, even if they dislike it):
public class X {
public void method() {
...
badMethod();
...
}
...
}
Class X has a method named badMethod. The method isn't bad because it does something awful while the system is running; it's bad because it does something painful during testing. It could be anything. The method could make a call to our production database and update it, or it could communicate with another system across a socket. It could even touch some low level hardware that causes a mechanical machine arm to do something that it shouldn't be doing over and over again while we are testing.
In an ideal world, people would design their systems so that classes and clusters of classes could be tested independently; the industry is slowly rediscovering the utility of unit testing. But, stuff happens. People make mistakes. They think that they've got it all under control until they discover that they have a big wad of code that they can't easily change and don't understand. At that point, they wish that they could write little tests that help them understand what the hell their code does before and after they change it.
The nice thing, so far, is that most of the object-oriented languages out there leave you an “out.” You can use the features that were built in to provide flexibility to give yourself enough maneuverability to test. In short, we can do this:
public class TestableX extends X {
void badMethod() {
// do nothing
}
}
We can override functionality and build ourselves a wedge that makes testing easy.
The fact that badMethod is a non-final, override-able method makes it something I call a seam. A seam is a place where you can substitute one piece of functionality for another without editing. Most languages provide seams. The C programming language is rich with them. You can use the preprocessor, function pointers, and link substitution to replace awkward functionality during testing, but it isn't quite as easy as it is in OO.
Virtual function calls in OO languages are a common seam. Java is easy. C++ is a bit more awkward. Ruby and Python are a breeze. The power of these seams is in the fact that you can get between around around the pieces of your application when testing. It isn't just a big hard-coded mass.
What about Haskell, OCaml, and Erlang?
Yes, you can provide alternative modules to link against, but it's clunky. Sure, you can use virtual in OCaml, but who really organizes all of their OCaml code in classes?
Haskell is a bit nicer, most of the code that you'd ever want to avoid in a test can be sequestered in a monad, and I expect that half of the Haskellers reading this will argue that functional purity obviates any need to unit test; it can all be done in QuickCheck. I hear you, but there's distance between here and there and that distance can be a quagmire.
No, the fact is, the late-binding that OO languages provide makes code written in them more easily recoverable. And, this is important because entropy happens. Count on it.

Comments (14)
Thanks for this post!
I've been following this blog silently for a while now, and some articles are, I have to admit, a bit too difficult for me to understand, but those that I could understand really helped me gain deeper insights, and this post is one of them.
Just thought I'd express my appreciation.
Keep them coming!
Posted by Timothy | February 21, 2008 6:35 PM
I found it indeed slightly disturbing that Programming Erlang by Joe Armstrong (Pragmatic Bookshelf, 2007) doesn't have anything on testing. This absence is particularly noteworthy given the publishers' otherwise strong focus on testing.
I don't intend to bash the Erlang as a language, but it is quite clear that writing tests for systems implemented in Erlang poses some new challenges over common OOPLs and other FPLs as it almost by necessity involves concurrency.
Posted by Michael Schuerig | February 21, 2008 7:13 PM
The first thought I had while reading your post was that tt wouldn't be hard to use a preprocessor of your choice (like say M4) with any of the functional languages you mentioned, except maybe Erlang if you were wanting to edit and compile automatically. Even that shouldn't be too hard to rig up though. Anyway, then you would at least be able to use the macro approach from the C world.
Posted by John | February 21, 2008 7:58 PM
Actually, Haskell makes things even better here, because there is a huge barrier to writing "bad things which could happen during testing" in the first place.
Indeed, if the module/typeclass doesn't have an "IO" in the type, then these bad situations simply can't happen.
Posted by Pseudonym | February 21, 2008 9:59 PM
Yes, having well placed seams will simplify your testing, but I think you perhaps presume too much when you claim they will be there merely by the grace of dynamic dispatch. What about the case where your code isn't factored into methods that align with the needs of your tests? If your badMethod() was instead just inline code in another method you would lose the seam you depend on:
Suddenly, you must change your implementation to accommodate the test - potentially a dangerous undertaking if you are in the situation where there aren't good tests in place already - or you must duplicate production code in your test code and face the maintenance issues of making sure that they behave identically and stay in sync.
I think your thoughts about "seams" are really just getting at the underlying issue of design for testability. It should be clear that in any idiom, having the "seams" of your design mesh well with the needs of testing it will greatly simplify the testing. So, the question becomes one of whether OO, or any other particular idiom, inherently tends to place your seams where you need them without thought from the programmer. I would say from my experience that is not the case, and you don't really present evidence to support such a claim.
Any language will have its sources of "seams" that let you instrument for testing. But whether they are dynamic methods, first class functions, module boundaries, monads, or anything else, I would ascribe their proper placement to the quality of the design and the designer far more than to the language or idiom in which they are implemented.
Posted by Andrew | February 21, 2008 11:19 PM
I saw your book at the bookstore and am increasingly glad I didn't buy it.
Posted by What a joke | February 22, 2008 12:14 AM
If I understand you correctly, you are talking about pluggability -- able to plug in various mock objects at various places so to isolate the object-under-test. Thus, we are talking about what mechanisms are provided in the lang in general to allow us to substitute portion of the logic. (I think this is called "points of variation".)
In OOP, the characterizing mechanisms are inheritance/polymorphism and delegation. Likewise in C, they are macros and function pointers, etc. In functional programming, it is high-order functions.
One may argue what if the code isn't written with structured with the proper high-order functions to provide the points of variation to plug in the mock functions. But we can argue equally, in your example, what if class X doesn't have other virtual functions one can override. The point is, regardless it's functional or OOP, the code needs to be structured to facilitate unit testing in the first place.
Am I making sense?
Posted by john | February 22, 2008 1:46 AM
John, that's fair enough. You can use a preprocessor. The only language I do that in (today) is C, and even then I usually opt for function pointers or link substitution first, primarily because it involves less head scratching than C's very unhygienic macros.
The thing that really annoys me is that this testing issue is an afterthought. Very few language designers consider it.
Posted by Michael Feathers | February 24, 2008 9:45 AM
Andrew, it is true that you often have to change your code to make it testable, but there are safe ways of doing it.
If you are working with a tool which performs adequate analysis before extract method, you can extract safely. If you don't, you can use sensing variables to perform the extraction. I often do this in C and C++ because of the "tool trust" factor.
It's true that seams don't always align with the grain of the testing that you want to do, but it definitely is easier in languages with good seam support both because of the seams that are there accidentally and because it is easier to open up new ones. I'm afraid I don't have any evidence on that, but I do have experience. I literally do little else these days other than travel around the world and help people get code under test, and the seams that are already there are very handy. Often you have a choice of levels when you approach testing problems. More seams equals == more possibilities. I'm not one to plug my book because I think that's tacky. I should get some of that material online.
I agree that "design for testability" is the real issue, but I also know (sadly) that no matter what we do, there will be systems where it doesn't happen. For that reason, recoverability is an issue that I care about a great deal.
Posted by Michael Feathers | February 24, 2008 10:24 AM
John, you said "One may argue what if the code isn't written with structured with the proper high-order functions to provide the points of variation to plug in the mock functions. But we can argue equally, in your example, what if class X doesn't have other virtual functions one can override. The point is, regardless it's functional or OOP, the code needs to be structured to facilitate unit testing in the first place."
True, but doing what it takes to make something testable after the fact is easier in some languages than it is in others. We can use the Java example as a foundation.
You point out that if badMethodwasn't virtual we would be out of luck. Nicely, though, in Java virtual is the default. You can make a method non-virtual by making it final, but it is easy enough to make a final method non-final when you need to (provided you own the code).
In Ruby, things are much easier. You can actually replace a "bad method" at the object level rather than the class level.
C++, on the other hand, is harder. If the member function is non-virtual, you can make it virtual, but you have to check other things. For instance, if the class has a base class, you have to check for non-virtual overrides of the same member function. Also, if there are no other virtual functions on the class, you have to consider a virtual destructor and other aspects of the class design to make sure they are consistent. In most cases you will end up changing the class's header file, which (if it is in a legacy system) often triggers a massive rebuild.
C is a bit easier, because you can play some tricks that, by the way, aren't available in some functional programming languages. There is a big difference between a function pointer and a first-class function. Function pointers are mutable. So, instead of changing a bunch of calls to pass a function down to the point where you want to replace it, you can just declare a function pointer with the same name as the function you want to replace and initialize it to a different function before the test call. The calling syntax is the same.
These language things do seem to matter when we are trying to recover.
Posted by Michael Feathers | February 24, 2008 10:40 AM
My understanding of the functional approach is that I should be writing, er, functions that take inputs and return outputs, so programming should be the act of assembling these components which should, in turn, provide seams for testing.
I can also believe that there might not be the same style of testing in the functional community, but then there didn't used to be in the OO world. As you point out, we've all seen enormous stream-of-consciousness methods with nowhere to get in.
I'd be very interested to hear from people working on significant functional code bases to see how they do it.
Posted by Steve Freeman | February 24, 2008 4:56 PM
Functional languages make it easy to structure your code with all that nasty stuff seperated off at the top-level and keep your core logic pure. Unless you're fighting against the functional language to write your code in a non-functional idiom, you just don't get the IoC and seperation-of-concern issues you do with imperative OO code.
And yes, Monads help a lot here too, effectively they let you write one piece of code that creates (on-the-fly) a list/stream of IO commands, and another piece of code consumes the stream and decides how to execute those commands.
The situation is pretty much the opposite to what you fear. You don't usually create those testing problems in the first place when you write functional code (if you have you should probably refactor right now). Unit testing is so much easier when your units are guaranteed truly independent! Or at worst the dependencies are clear and explicit.
Posted by Greg M | February 24, 2008 8:40 PM
Let me apologize if this seems like a functional programming bigot email, but I was honestly boggled by this article, since my experience with testing tends to lead me to the opposite conclusion from yours. That is, my impression is that the heavy use of state in conventional object-oriented programming is detrimental to testing. Since you are obviously a thoughtful and experienced maker of software, this suggests that there may be points in both direction.
I am primarily a Common Lisp programmer now, although at one time I wrote C code for a living. As a CL programmer I am not a pure functional programmer, as a Haskell programmer might be --- Common Lisp (like ML, and OCaml, which you describe as functional) is a multi-paradigm programming language.
My experience in writing tests is that the functional style can be immensely helpful in simplifying testing. When my code is well structured, then tests can typically be structured in terms of function calls and comparisons between the results of the functions and expected results. Of course that is oversimplifying --- there are issues of setup and teardown, dynamic scoping, and checking for conditions, among other things --- but in general the tests are simple to write. My experience in dealing with heavily object-oriented CL code is less happy --- writing tests is complicated by the need to build up large bodies of interconnected objects in order to set the stage for the tests, and there may be additional complications in checking that the expected side-effects have occurred.
I believe that my impression is substantiated by all the discussion of "mocks" and so forth in the OO testing literature.
As someone who uses a hybrid of functional and OO programming, I'm also a bit confused by all the discussion of the need for injecting "mock functions" for testing. When testing functional aspects of my code, I just don't find myself encountering this problem. I don't wish to use mock functions; I simply test the real functions. Since they are generally not side-effecting, there is no need to use a mock function.
In those cases where there are side-effects, and one might want the equivalent of a mock function, surely the behaviors one wants would be encapsulated in separate program entities to permit swapping in and out? What functional programming system would make this hard? In a purely functional framework such as Haskell, any of these problems would have to be encapsulated in a Monad; there's simply no other way to handle side-effects, so one would simply need some "mock Monad." I'm not that much of an Ocaml programmer, but I found it hard to follow your claims about that language. Indeed, I'm inclined to think it's precisely the non-functional, side-effecting and object-oriented aspects of Ocaml that are the ones that create the problems to which you refer, or perhaps more specifically the combinations of Ocaml's type discipline and its object oriented features.
Common Lisp, of course, has OO constructs, but as I said above, I find their use to be more a hindrance than a help in testing. The OO-related constructs of Common Lisp that seem to me to be most helpful are the ones that are more commonly referred to as "Aspect Oriented." In particular, I find the ability to dispatch on multiple arguments very helpful (to abuse your terminology, this might be called "even later dispatch"), and the ability to create around methods is very handy for test fixtures. But neither of these is offered by more conventional OO frameworks.
As an outsider to the programming community from which you come, it seems to me almost that you are complaining that functional programming frameworks make it hard to test programs with side-effects. But of course, those are precisely the kinds of programs that are either avoided (by constructs like the Monad) or that are admitted by non-functional parts of hybrid languages like ML, Ocaml, and Common Lisp.
Part of the reason I don't get your argument is that I'm writing different sorts of program from ours, using different tools. This is certainly substantially my fault. But surely this is also a problem with your argument --- I think there must be some assumptions there that are so obvious to you that you felt that they didn't need stating.
Posted by Robert Goldman | February 24, 2008 10:55 PM
Even though the seam may not be present in a FP function
method = do ... badMethod ... return xit seems straight forward to create a seam
Now all testing can be carried out on method' without those side effects in badMethod that are undesirable for testing by substituting in a dummy or mock 'f'
Posted by Levi | March 26, 2008 12:14 AM