Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write useful unit tests for a mostly service-oriented app?

Tags:

I've used unit tests successfully for a while, but I'm beginning to think they're only useful for classes/methods that actually perform a fair amount of logic - parsers, doing math, complex business logic - all good candidates for testing, no question. I'm really struggling to figure out how to use testing for another class of objects: those which operate mostly via delegation.

Case in point: my current project coordinates a lot of databases and services. Most classes are just collections of service methods, and most methods perform some basic conditional logic, maybe a for-each loop, and then invoke other services.

With objects like this, mocks are really the only viable strategy for testing, so I've dutifully designed mocks for several of them. And I really, really don't like it, for the following reasons:

  1. Using mocks to specify expectations for behavior makes things break whenever I change the class implementation, even if it's not the sort of change that ought to make a difference to a unit test. To my mind, unit tests ought to test functionality, not specify "the methods needs to do A, then B, then C, and nothing else, in that order." I like tests because I am free to change things with the confidence that I'll know if something breaks - but mocks just make it a pain in the ass to change anything.
  2. Writing the mocks is often more work than writing the classes themselves, if the intended behavior is simple.
  3. Because I'm using a completely different implementation of all the services and component objects in my test, in the end, all my tests really verify is the most basic skeleton of the behavior: that "if" and "for" statements still work. Boring. I'm not worried about those.

The core of my application is really how all the pieces work together, so I'm considering ditching unit tests altogether (except for places where they're clearly appropriate) and moving to external integration tests instead - harder to set up, coverage of less possible cases, but actually exercise the system as it is mean to be run.

I'm not seeing any cases where using mocks is actually useful.

Thoughts?

like image 279
levand Avatar asked Oct 14 '09 17:10

levand


1 Answers

If you can write integration tests that are fast and reliable, then I would say go for it. Use mocks and/or stubs only where necessary to keep your tests that way.

Notice, though, that using mocks is not necessarily as painful as you described:

  1. Mocking APIs let you use loose/non-strict mocks, which will allow all invocations from the unit under test to its collaborators. Therefore, you don't need to record all invocations, but only those which need to produce some required result for the test, such as a specific return value from a method call.
  2. With a good mocking API, you will have to write little test code to specify mocking. In some cases you may get away with a single field declaration, or a single annotation applied to the test class.
  3. You can use partial mocking so that only the necessary methods of a service/component class are actually mocked for a given test. And this can be done without specifying said methods in strings.
like image 181
Rogério Avatar answered Oct 11 '22 15:10

Rogério