First let me state that, despite being a fairly new practitioner of TDD, I'm pretty much sold on its benefits. I feel like I've progressed enough to consider using mocks and have hit a real brick wall when it comes to understanding where mocks fit in with OOP.
I've read as many relevant posts/articles on the subject as I could find (Fowler, Miller) and am still not totally clear on how or when to mock.
Let me give a concrete example. My app has a service layer class (some call it an application layer?) in which methods map roughly to specific use cases. These classes may collaborate with the persistence layer, the domain layer and even other service classes. I've been a good little DI boy and have properly factored out my dependencies so they can be subbed for testing purposes etc.
A sample service class might look like this:
public class AddDocumentEventService : IAddDocumentEventService
{
public IDocumentDao DocumentDao
{
get { return _documentDao; }
set { _documentDao = value; }
}
public IPatientSnapshotService PatientSnapshotService
{
get { return _patientSnapshotService; }
set { _patientSnapshotService = value; }
}
public TransactionResponse AddEvent(EventSection eventSection)
{
TransactionResponse response = new TransactionResponse();
response.Successful = false;
if (eventSection.IsValid(response.ValidationErrors))
{
DocumentDao.SaveNewEvent( eventSection, docDataID);
int patientAccountId = DocumentDao.GetPatientAccountIdForDocument(docDataID);
int patientSnapshotId =PatientSnapshotService.SaveEventSnapshot(patientAccountId, eventSection.EventId);
if (patientSnapshotId == 0)
{
throw new Exception("Unable to save Patient Snapshot!");
}
response.Successful = true;
}
return response;
}
}
I went through the process of testing this method in isolation of its dependencies (DocumentDao, PatientSnapshotService) by using NMock. Here's what the test looks like
[Test]
public void AddEvent()
{
Mockery mocks = new Mockery();
IAddDocumentEventService service = new AddDocumentEventService();
IDocumentDao mockDocumentDao = mocks.NewMock<IDocumentDao>();
IPatientSnapshotService mockPatientSnapshot = mocks.NewMock<IPatientSnapshotService>();
EventSection eventSection = new EventSection();
//set up our mock expectations
Expect.Once.On(mockDocumentDao).Method("GetPatientAccountIdForDocument").WithAnyArguments();
Expect.Once.On(mockPatientSnapshot).Method("SaveEventSnapshot").WithAnyArguments();
Expect.Once.On(mockDocumentDao).Method("SaveNewEvent").WithAnyArguments();
//pass in our mocks as dependencies to the class under test
((AddDocumentEventService)service).DocumentDao = mockDocumentDao;
((AddDocumentEventService)service).PatientSnapshotService = mockPatientSnapshot;
//call the method under test
service.AddEvent(eventSection);
//verify that all expectations have been met
mocks.VerifyAllExpectationsHaveBeenMet();
}
My thoughts on this little foray into mocking are as follows:
- This test appears to break many fundamental OO precepts, not least of which is encapsulation: My test is thoroughly aware of the specific implementation details of the class under test (i.e method calls). I see alot of unproductive time spent updating tests whenever class internals change.
- Maybe its because my service classes are fairly simplistic at the moment, but I can't quite see what value these tests add. Is it that I'm guaranteeing that collaborating objects are being invoked as the specific use case dictates? The code duplication seems absurdly high for such a small benefit.
What am I missing?