I tend to do mock-heavy testing in Java projects, and it has burned me several times. You write mock tests along your architectural boundaries, but if those need to change due to a deeper refactoring, all your tests need to get refactored as well.
Mock testing essentially tests expected side effects. A more powerful concept is the use of pure functions wherever possible, so that your tests compare input/output pairs, instead of long, difficult to maintain and sometimes non-exhaustive lists of expected side effects.
Does anybody know how one can replace Services, Controllers, Presenters and other such imperative mediator objects with a more functional approach? I'm just speculating, but that should make test maintenance easier.
You might be interested in the "free monad" approach used occasionally in scala and haskell. It separates the effectful operations from the effects by first building a data structure that describes what to do and then interpreting that data structure. The key is that you can interpret the data structure in different ways. That is, one interpreter for production and another for testing. The advantage this has over mocking is that the business logic becomes a collection of pure functions.
Take a look at Gary Bernhardt's talk "Boundaries"[1]. It touches on this very topic and was quite the eye-opener for me (having experienced all the same issues you've described).
Monads. Possibly the Free Monad. Make your business logic pure logic that computes commands, and separate the computation of the command from its execution, then you can use a different execution to test your command chains. http://michaelxavier.net/posts/2014-04-27-Cool-Idea-Free-Mon... has a basic example, but it works well for replacing your services/controllers/what-have-you too.
I find that you can keep with a class-based approach if you utilise your IOC container in your tests, and only swap out side-effectful classes (ORM, remote service clients, etc) with mocks. That way you don't need to concern yourself with the dependencies of the classes you're testing so you can refactor the underlying relationships and objects with impunity as long as you don't change the interfaces of the classes you're testing.
I don't know if I asked my question as clearly as possible. Mocking your DB, the file system, the remote API, etc are all a given when doing tests. My question was aimed at trying to find out if it's feasible to replace the imperative Presenter/Service/etc patterns with a more functional approach.
What I'd like to see is testing only the returned value of a function, as opposed to calling a void function and checking whether the injected mocks got the proper method invocations, i.e. whether the desired side effects occurred.
if you're calling a void function then by definition you're calling a function that does nothing but side effects or calling off to other classes. In the latter case, they can be good candidates for refactoring in my experience. A function that does something but doesn't either produce mockable side-effects or return information may be a code smell.
A function can return void and still throw an exception. (Like one I wrote today, "insert row into table", which throws on failure and return nothing on success.)
A bunch of functions that all return void all being called in a row, though? That's a much smellier smell. What happens if their order gets mixed up, for example.
Testing that function would be a matter of mocking out the database with a class that can emulate the type of failure that would trigger your exception.
it's almost more worthwhile than testing your happy path - when things go wrong you want to have guarantees that they'll be handled correctly and not kill your server.
Mock testing essentially tests expected side effects. A more powerful concept is the use of pure functions wherever possible, so that your tests compare input/output pairs, instead of long, difficult to maintain and sometimes non-exhaustive lists of expected side effects.
Does anybody know how one can replace Services, Controllers, Presenters and other such imperative mediator objects with a more functional approach? I'm just speculating, but that should make test maintenance easier.