Mock Objects caught the developer imagination when they were introduced to the world in 2000 and there are mock object frameworks for almost as many languages as there are testing frameworks. Many teams however discover too late the pain caused by using mock objects in their tests; brittle tests, an increasing lack of confidence in the tests and an inability to safely refactor. This talk explores the sources of pain caused by using mock objects and presents an alternative that is increasingly gaining in popularity.
For the first 23 minutes I was thinking to myself, “Yes, but what about Mockito?”…
Thanks for this presentation. I had thought that Mockito was only an easier-to-use equivalent of EasyMock or JMock. I now realise that underspecifying by default instead of overspecifying by default, though it seems like a minor difference, is of huge importance. I was only vaguely aware that I was now writing tests with stubs and spies instead of with mocks. I daresay most people will continue to call them mocks.
Mockito doesn’t answer all of your concerns. It’s still easy to write brittle tests with Mockito: for example, there might be 3 ways to call your DAO in order to get the same result, and you might switch between them looking for a performance improvement. This will still break your test.
The answer to this difficulty is define the interfaces between classes in terms of what you want to achieve, rather than how you’re going to achieve it. To illustrate, here’s the technique we use in my team to test DAOs and the services that depend on them. Rather than have a bunch of generic methods on a DAO, we define a dedicated method for each of the specific cases we need in our service layer. For instance, rather than findByQuery(String query), defined on a DAO superclass, we would have findPersonByCriteria(PersonCriteria criteria), defined on PersonDao.
When I want to test the service, I can use a stub of the DAO method without worrying about whether it uses an HQL query, native SQL or Hibernate Criteria API. When I want to test the DAO method itself, I test it against a real database (containing known test data), but again I can switch the implementation from HQL to native SQL without changing my test.
This is a good compromise for test performance too. Tests on the service layer can legitimately use only mocks, and run quickly. The persistence tests are rather slow, but I’m convinced you need to run the lowest level of your persistence code against a real database anyway (the principle of “Don’t mock something you can’t change”). It’s by far the easiest way to check not only that the (usually trivial) code in the DAOs is OK, but also that your OR mapping configuration is correct and coherent with your database schema.