What unit testing is in languages that don't have a stronger type system is simply working around warts in language design. Just as ORM systems are a "work around" for the warts in interfacing relational data with an object model.
[EDIT] I didn't write out my thought well enough: unit testing in "other languages" isn't necessarily working around the warts in language design but working around the warts in programmer thinking and process. Strongly typed languages don't solve those problems but they do improve the situation dramatically with their type guarantees and advanced testing systems (QuickCheck) therefore reducing the amount of cognitive overhead and code maintenance involved in writing tests. With languages like Haskell you only write Unit Tests for things that are difficult or complex to test using QuickCheck.
Side-effects create additional problems, but functional programming doesn't solve all problems inherent in programming.
In Haskell, the type system--thanks to typeclasses--can figure out how to generate random inputs automatically, just based on what type is expected. Even if you have a custom type, it's incredibly easy to define how to generate elements and then use it everywhere. Or you could easily randomly generate values in an ad-hoc method wherever they're used.
This takes advantage of the fact that in Haskell, values can be polymorphic on their return types. As far as I know, this is a feature unique to typeclasses--and very few languages aside from Haskell support those. (I only know of one other: Rust.) This, combine with Haskell's usual popular abstractions like Applicative Functors, makes writing QuickCheck-style properties and new random generators much easier.
Of course, this particular reason has nothing to do with functional programming and everything to do with Haskell's glorious type system. However, functional programming does play a role in QuickCheck's popularity as well.
Basically, QuickCheck-style tests are primarily useful for code that does not have side effects. QuickCheck lets you check some invariant by evaluating the same function hundreds or thousands of times with randomly generated input. This style simply does not scale to non-trivial non-local state, much less actual IO.
So even if your language had a usable QuickCheck library, it would only be useful for the more functional parts of your code. As the article points out, unit tests push you towards a functional style to some extent. QuickCheck does this far more. And so most people writing rampantly imperative code in wantonly imperative languages simply don't use QuickCheck. Even if some intrepid soul has ported it.
No. The reason why the type system is able to generate these values is because of algebraic data types, not type classes.
Type classes are completely unrelated to this problem.
The way the Arbitrary class works is only possible thanks to typeclasses. Without typeclasses, you would have to explicitly specify which generator to use. Thanks to the typeclass system, Haskell can just infer this.
On the other hand, none of the Arbitrary machinery is particularly tied to algebraic data types per se. It would work as well if you had OO style constructors instead.
Just take a look at the OCaml version of QuickCheck to see what I mean. OCaml obviously has algebraic data types, but it's still far more awkward to use QuickCheck there.
QuickCheck is NO SUBSTITUTE for real unit testing. All it does is automatically let you randomize inputs since it can know the input type. Unit tests shouldn't even have randomized inputs, they should be deterministic.
Unit tests that verify the in-order invocation of several different void methods have nothing to do with reinvention of purity.
I think a better argument would be: the best way to write testable units is to write pure functions. This argument was made for example in Hughes's Why Functional Programming Matters. But it's not really a question of OO vs FP.
There's a seemingly important pattern that I don't know if there's a common name for. It's something like "Replace Side-Effect With Value." Or maybe "Inversion of Side-Effecting." This is relevant even in Haskell. Either you write your whole program as a big messy monad transformer stack with IO at the top -- I don't know how to unit test that, much less prove anything -- or you define the application logic as pure functions that give instructions to a simple driving "side-effect interpreter."
XMonad, the Haskell window manager, is written in this latter way, with lots of unit tests too. There's a cool video walkthrough of how the code works -- http://www.youtube.com/watch?v=63MpfyZUcrU -- and a document called Guided tour of the xmonad source -- http://www.haskell.org/haskellwiki/Xmonad/Guided_tour_of_the....
* It's not always about the tests. I TDD code (functional as well as OO - hell procedural come to that) as a design tool - not a testing tool. I write acceptance tests as tools to help us understand when we reach "done" and as ways to track progress.
* It's about stopping regressions. Test suites get in the way of accidental behaviour changes.
* There a way to scale team communication of how the code should behave.
I love FP dearly, but if it replaced testing why do I see so much FP code with test suites ;-)