Halfway between single class unit testing and end to end acceptance testing is a grey area that for now I’m calling Unit Acceptance Testing. Basically it’s a unit test that instead of isolating a single class or layer under test, tests several layers and the way that they interact.
For example, we had a an MVC web application that used a business object layer that sat on top of a database layer, built from a code generator. A unit test of the business object would have used a mock database layer to exercise the rules in the business objects. We had those, but we still had weird bugs at the UI level. So we started making tests that exercised the business objects but used the real database. This let us identify a bunch of problems that were plaguing other layers in the system that would have been tough to find with traditional integration tests.
When I talked to one of the other developers about this he was calling these tests integration tests and I was calling them unit tests. Either way, it made fixing the problems fairly easy.
Writing code in the higher level layers, you’d encounter some sort of bad behavior. First you’d see look at the unit test for the behavior, frequently the unit test was fine. So you’d write the test that exercised both layers, and see it fail. Usually the culprit was that there would be optional information missing in the call to the next layer down the stack. Nothing was wrong, it just wasn’t completely right yet. That’s the kind of bug that traditional unit testing would ignore because your mocked object would return the correct results.
This approach reduced the amount of time it took to isolate and fix problems. By showing a test that demonstrated the root cause of the problem, the usual debate about where in the layers of the application the problem started was eliminated. Since there was an automated test for it, regression was virtually impossible.
The other surprising benefit to this approach was that performing the root cause analysis was surprisingly simple. So when we would encounter a data problem in the web app we’d start with a unit acceptance test there. The controller unit test would demonstrate the problem and give you a couple of items to configure to check if the problem was the inputs at that level. Each successive level of testing would let you isolate the inputs to the layer to determine if the problem was something like optional arguments being ignored or something getting called out of order.