top | item 493963

Writing unit tests is reinventing functional programming in non-functional languages

93 points| jamongkad | 17 years ago |erlang.org | reply

46 comments

order
[+] akeefer|17 years ago|reply
I'd say that it's true that the closer you get to pure functional programming (no state or side effects), the more unit testable your code becomes.

I'd have to take issue, however, with the author's implication that Java programmers who unit tests are idiots who don't realize they're reinventing the wheel. Good developers in any language recognize the importance of avoiding side effects, isolating parts of the system from each other (including minimizing state), etc. It shouldn't come as a surprise to anyone that testable code has some common characteristics across all programming languages. There are differences unique to the language as well, though, and unit testing in Java also tends to involve use of things like interfaces and polymorphism that are distinctly OO concepts as well.

And of course, unit testing isn't just for those "overly complicated" Java applications; it's for any type of system in any language. The author seems to me to imply that somehow unit testing is uniquely a requirement of Java systems that Erlang systems somehow don't need.

[+] sayrer|17 years ago|reply
The main point the guy makes is that "testable code" resembles functional programming. And testable code is code that doesn't suck. Maybe code that doesn't suck is code that resembles functional programming.

I guess you could argue that all the moronic OO voodoo theory books are trying to say this. I don't think so. I think he's a got a point.

[+] 10ren|17 years ago|reply
I agree that avoiding side effects and global state is a well-known practice (and OO languages have access modifiers, like private, to support it). It's not unique to functional programming.

Interesting to me was that the reasoning of the article was strikingly close to Dijkstra's "Goto considered harmful", and it makes me wonder if there is some small step that OO languages could take to automatically enforce testable code, in the way that structured programming made gotos safer.

The problem is that side-effects and global state are very useful for making some problems simpler - which is why Erlang has its transactional database mnesia, and Lisp is not purely functional.

Is there a way to structure side-effect/global state to make it entirely testable, without destroying the benefits?

[+] nostrademons|17 years ago|reply
This actually is what drew me to functional programming in the first place. I'd recently been on a unit testing kick, and figured that if you get rid of mutable state and have function outputs depend only upon inputs, your code suddenly becomes much easier to unit test.
[+] unknown|17 years ago|reply

[deleted]

[+] arebop|17 years ago|reply
Referential integrity in code-under-test is convenient for unit testing, so unit tests encourage (re)discovery of functional style by programmers who are working in non-functional languages.

But unit tests also have value in programs that are written in functional languages and in a functional style. Unit tests reinforce essential properties of the program and thereby formalize the accidental/essential boundary. They can provide guidance about how API elements are intended to be used together. They can present elements and properties of a program in an order that is best for explanation (they can be a crude form of literate programming). So, it's wrong to reduce unit testing to functional programming.

[+] neilk|17 years ago|reply
Look up the technique of "dependency injection" sometime. This is all the rage among Java consultants, their favorite technique to make things testable. It's an elaborately disguised way of saying "let's rewrite this function so it obtains all its state from the caller". In other words, functional programming.

In cases where passing the same list of initialized objects up and down the call stack becomes tedious, Google's Java people have invented frameworks like Guice. This makes dependency injection appear to happen by magic.

[+] vlisivka|17 years ago|reply
Dependency Injection allows to assemble program on the fly. It allows to assemble programs written in different styles, including functional.

Look at Spring Dependency Injection Framework for example. It supports lot of libraries, which are written in traditional style.

[+] jcbozonier|17 years ago|reply
So... I am an avid TDD/Unit Test/BDD guy. I've been gravitating towards BDD lately and honestly I don't DI everything. Really if I'm just BDD-ing a layer I just need to DI the fringe dependencies, the things that would cause me to be dependent on other whole systems or sub-systems (think File I/O, Active Directory, Web Services, etc). My object still contains a lot of state.

I think saying that unit tests cause us to reinvent FP is a bit of a stretch. I do think that saying that unit testing causes us to write better code and we borrow heavily from many programming paradigms (DBC, FP, OOP, DSLs, etc) in order to write that better code.

[+] lacker|17 years ago|reply
I think you have overdosed on acronyms.
[+] sdp|17 years ago|reply
Interesting point, although I would argue that writing unit tests is more like reinventing Design by Contract.
[+] gnaritas|17 years ago|reply
Not really, contracts can't send in invalid data to see what happens and ensure the proper actions are taken.

Contracts don't prevent the need for tests, they simply augment them. Contracts only state assumptions about how code should behave, but they don't make sure those assumptions are correct, only tests can do that.

[+] jaspertheghost|17 years ago|reply
I really like functional languages while others hate it, but I don't think functional languages will take off by itself. The reason why functional languages don't work is that they're they wrong metaphor. Object oriented programming works because it fits the user model of how to describe actions and the world.

Features of functional languages will influence PL (Ruby, EMCA script, Python), server paradigms (map reduce), but I don't think it'll ever go mainstream by itself.

[+] drubio|17 years ago|reply
Better put 'functional programming is reinventing the way unit tests are written'. Its extremely easier to 'think of' and create unit tests on business logic that avoids using state and mutable data, as do all functional languages.

On the other hand, this is pretty much offset because structuring a program ( the one on which the tests will be performed) using a pure functional style is very hard, especially when its syntax/compiler doesn't enforce it.

Languages like Java which have fields, getter/setter methods and similar syntax are mine-fields for introducing state and mutable data. Erlang really forces you to think in functional style given its 'message passing' style, so unit tests are a snap. In Java and other non-functional languages unit tests would be just as easy, the problem is avoiding state and mutable data.

Though I would still argue the effort forgone in unit testing, still needs to be invested designing the actual business logic taking care it doesn't introduce state and mutable data(non-functional behavior).

[+] donw|17 years ago|reply
This will sound like a trollish comment, but can anyone point to a modern web-app written in a purely functional language?

I'm curious how several design problems, such as DB access and authentication, have been handled...

[+] ibsulon|17 years ago|reply
I appreciate side effect programming, but I really think the java criticism is unwarranted. The first thing I think of is: "How do you do a CRUD app in your functional language?"

I enjoyed ML when we were doing it in school. However, I've only worked on one application that could be translated easily to the functional paradigm. The rest have parts that would be well suited to functional programming, but the rest of the program is one giant side effect. Monads aren't the answer there, they're a workaround.

[+] joe_the_user|17 years ago|reply
A LOT of "ordinary programming" is moving some messy bits of information from one place to another, taking into account some messy cases and occasionally putting information into a third, messy place.

This kind of programming has little mathematical content and little similarity to the canonical factorial example that every functional programming language LOVES to give.

Here, functional programming seems to lose its elegant minimalism. Indeed, when you have a bunch of arbitrary, (meaning random) cases and conditions, no language can make your code elegant and minimal since your code must contain the information in the cases and random information is incompressible.

I mean, how would you write a WIN32 GUI in a functional language?

Show me otherwise but I haven't seen the good argument for functional programming languages as general purpose languages and so the folks dumping Java don't really seem very much better.

[+] jrockway|17 years ago|reply
How do you do a CRUD app in your functional language?

I'm not going to give you a long tutorial on this, but it's not hard. You write it like you would anything else.

You have some data that you want to manipulate. You define some way to store, edit, and retrieve that data. (Libraries can handle this for you.)

Then, you make the web UI like you would in any other language. At the top is a function that takes a request and returns a response, which is entirely pure. Then you implement the helper functions as needed; Map URIs to handlers, map request parameters to data to interact with, interact with the data, generate a response, etc.

Anyway, it is not that different from anything else. Why would it be?

the rest of the program is one giant side effect. Monads aren't the answer there, they're a workaround.

You are thinking about the problem wrong.

[+] richcollins|17 years ago|reply
There is no reason that side effects have to be difficult to debug. You can think of the lookup chain for a variable as being part of a data structure being passed in to a function. It is just as easy to design a convoluted data structure that gets passed in to a function as it is to design a convoluted object hierarchy.

The difference is that you can't get implicit lookups for free in an fp language unless you implement something like messageSend(name, obj, args).

[+] apgwoz|17 years ago|reply
This is unrelated to the post, but I noticed that instead of removing the email addresses from the thread, they've replaced them with images that are using a vary standard looking monospace font. I would think that any program that mentions "OCR" (including the 20 minute template matching hack you could do in Python with PIL), would be able to harvest addresses from it...
[+] llimllib|17 years ago|reply
I'll have to tell those haskell guys to throw quickcheck away then, since they don't need unit testing...
[+] sketerpot|17 years ago|reply
Did you just read the headline? Maybe a better phrasing would be "The need for unit testing is causing OOP guys to write in a functional programming style."
[+] gaius|17 years ago|reply
Loads of "modern" stuff - unit testing, design patterns, continuous integration, aspect-oriented programming, etc - is people trying to retrofit FP ideas back onto OO code.

This is far, far more work than just using a functional language in the first place!