What are the pros and cons of automated Unit Tests vs automated Integration tests

automated-testsintegration-testingunit testing

Recently we have been adding automated tests to our existing java applications.

What we have

The majority of these tests are integration tests, which may cover a stack of calls like:-

  1. HTTP post into a servlet
  2. The servlet validates the request and calls the business layer
  3. The business layer does a bunch of stuff via hibernate etc and updates some database tables
  4. The servlet generates some XML, runs this through XSLT to produce response HTML.

We then verify that the servlet responded with the correct XML and that the correct rows exist in the database (our development Oracle instance). These rows are then deleted.

We also have a few smaller unit tests which check single method calls.

These tests are all run as part of our nightly (or adhoc) builds.

The Question

This seems good because we are checking the boundaries of our system: servlet request/response on one end and database on the other. If these work, then we are free to refactor or mess with anything inbetween and have some confidence that the servlet under test continues to work.

What problems are we likely to run into with this approach?

I can't see how adding a bunch more unit tests on individual classes would help. Wouldn't that make it harder to refactor as it's much more likely we will need to throw away and re-write tests?

Best Answer

Unit tests localize failures more tightly. Integration-level tests more closely correspond to user requirements and so are better predictor of delivery success. Neither of them is much good unless built and maintained, but both of them are very valuable if properly used.


(more...)

The thing with units tests is that no integration level test can exercise all the code as much as a good set of unit tests can. Yes, that can mean that you have to refactor the tests somewhat, but in general your tests shouldn't depend on the internals so much. So, lets say for example that you have a single function to get a power of two. You describe it (as a formal methods guy, I'd claim you specify it)

long pow2(int p); // returns 2^p for 0 <= p <= 30

Your test and your spec look essentially the same (this is sort of pseudo-xUnit for illustration):

assertEqual(1073741824,pow2(30);
assertEqual(1, pow2(0));
assertException(domainError, pow2(-1));
assertException(domainError, pow2(31));

Now your implementation can be a for loop with a multiple, and you can come along later and change that to a shift.

If you change the implementation so that, say, it's returning 16 bits (remember that sizeof(long) is only guaranteed to be no less than sizeof(short)) then this tests will fail quickly. An integration-level test should probably fail, but not certainly, and it's just as likely as not to fail somewhere far downstream of the computation of pow2(28).

The point is that they really test for diferent situations. If you could build sufficiently details and extensive integration tests, you might be able to get the same level of coverage and degree of fine-grained testing, but it's probably hard to do at best, and the exponential state-space explosion will defeat you. By partitioning the state space using unit tests, the number of tests you need grows much less than exponentially.