首先让我声明,尽管我是一个相当新的 TDD 实践者,但我对它的好处非常满意。我觉得我已经取得了足够的进步,可以考虑使用模拟,并且在了解模拟在 OOP 中的位置时遇到了真正的障碍。
我已经阅读了尽可能多的关于该主题的相关帖子/文章(Fowler,Miller),但仍然不完全清楚如何或何时嘲笑。
让我举一个具体的例子。我的应用程序有一个服务层类(有人称它为应用程序层?),其中方法大致映射到特定用例。这些类可以与持久层、域层甚至其他服务类协作。我一直是一个很好的 DI 小男孩,并且已经正确地分解了我的依赖项,因此可以将它们替换为测试目的等。
示例服务类可能如下所示:
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;
}
}
我通过使用 NMock 隔离了它的依赖项(DocumentDao、PatientSnapshotService)来测试这个方法的过程。这是测试的样子
[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();
}
我对这个嘲弄的小尝试的想法如下:
- 这个测试似乎打破了许多基本的 OO 规则,尤其是封装:我的测试完全了解被测类的具体实现细节(即方法调用)。每当类内部发生变化时,我都会看到很多非生产性的时间花在更新测试上。
- 也许是因为我的服务类目前相当简单,但我不太清楚这些测试增加了什么价值。我是否保证正在按照特定用例的要求调用协作对象?对于这么小的好处,代码重复似乎高得离谱。
我错过了什么?