I have the following challenge, and I haven't found a good answer. I am using a Mocking framework (JMock in this case) to allow unit tests to be isolated from database code. I'm mocking the access to the classes that involve the database logic, and seperately testing the database classes using DBUnit.
The problem I'm having is that I'm noticing a pattern where the logic is conceptually duplicated in multiple places. For example I need to detect that a value in the database doesn't exist, so I might return null from a method in that case. So I have a database access class which does the database interaction, and returns null appropriately. Then I have the business logic class which receives null from the mock and then is tested to act appropriately if the value is null.
Now what if in the future that behavior needs to change and returning null is no longer appropriate, say because the state has grown more complicated, so I'll need to return an object that reports the value doesn't exist and some additional fact from the database.
Now, if I change the behavior of the database class to no longer return null in that case, the business logic class would still appear to function, and the bug would only be caught in QA, unless someone remembered the coupling, or properly followed the usages of the method.
I fell like I'm missing something, and there has to be a better way to avoid this conceptual duplication, or at least have it under test so that if it changes, the fact that the change is not propagated fails a unit test.
Any suggestions?
UPDATE:
Let me try to clarify my question. I'm thinking of when code evolves over time, how to ensure that the integration doesn't break between the classes tested via the mock and actual implementation of the classed that the mock represents.
For example, I just had a case where I had a method that was originally created and didn't expect null values, so this was not a test on the real object. Then the user of the class (tested via a mock) was enhanced to pass in a null as a parameter under certain circumstances. On integration that broke, because the real class wasn't tested for null. Now when building these classes at first this is not a big deal, because you are testing both ends as you build, but if the design needs to evolve two months later when you tend to forget about the details, how would you test the interaction between these two sets of objects (the one tested via a mock vs the actual implementation)?
The underlying problem seems to be one of duplication (that is violating the DRY principle), the expectations are really kept in two places, although the relationship is conceptual, there is no actual duplicate code.
[Edit after Aaron Digulla's second edit on his answer]:
Right, that is exactly the kind of thing I am doing (except that there is some further interaction with the DB in a class that is tested via DBUnit and interacts with the database during its tests, but it is the same idea). So now, say we need to modify the database behavior so that the results are different. The test using the mock will continue to pass unless 1) someone remembers or 2) it breaks in integration. So the stored procedure return values (say) of the database are essentially duplicated in the test data of the mock. Now what bothers me about the duplication is that the logic is duplicated, and it is a subtle violation of DRY. It could be that that is just the way it is (there is a reason for integration tests after all), but I was feeling that instead I'm missing something.
[Edit on starting the bounty]
Reading the interact with Aaron gets to the point of the question, but what I'm really looking for is some insight into how to avoid or manage the apparent duplication, so that a change in the behavior of the real class will show up in the unit tests that interact with the mock as something that broke. Obviously that doesn't happen automatically, but there may be a way to design the scenario correctly.
[Edit on awarding the bounty]
Thanks to everyone who spent the time answering the question. The winner taught me something new about how to think about passing the data between the two layers, and got to the answer first.