So, after a 30 minute discussion with my coworker, I found and read Approaches to Mocking by Simon Stewart from the link on this site.
But, it too easily dismisses the static vs. dynamic mock objects argument with the ol' "be careful" rule. Which, if any of us were good at, we wouldn't need unit testing.
The real problem is how much can we test in a "black hole" method. For example:
public interface UserService {
public void saveUser(User user);
}
public class UserServiceImpl implements UserService {
private UserPersistence userPersistence;
public UserServiceImpl(UserPersistence userPersistence) {
this.userPersistence = userPersistence;
}
public void saveUser(User user) {
if (user.isPersistent()) {
userPersistence.insert(user);
} else {
userPersistence.update(user);
}
}
}
public interface UserPersistence {
...
}
How much of UserServiceImpl's saveUser() method do I need to test? With EasyMock, there is a very real tendency for ensuring the methods on the mocked UserPersistence object are called exact number in the exact order - leading to horrible long brittle tests that simply duplicate the internal structure of the method - the exact thing we're probably changing when we're working on the code. So that's not an option. More work needs to be done to properly write a unit test that isn't re-doing the method body using EasyMock (extra calls to anyTimes or whatever).
Even with static mock objects, what should I be verifying? Is this still a question of testing inputs and outputs, with the outputs being whatever is stored on the mock UserPersistence object, regardless of which calls were made? Should I check what objects were inserted vs. updated? Make sure that no other calls were made? Verifying the state of the mock "database" through the mock UserPersistence dependency? If the questions to that are all yes, then it seems like I'm back in EasyMock land, making sure that delete wasn't called but update was etc etc.
My coworker was arguing that the only thing a unit test can test is whether the code throws an Exception, returns the expected object, or modifies input parameters - any verification of the state or data of the object means that its a data verification test and not a unit test. Maybe it's just a semantic difference. Or philosophic.
Thanks.