最初に、TDD の初心者であるにも関わらず、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 の原則を破っているように見えます。カプセル化は特にそうです。私のテストは、テスト対象のクラスの特定の実装の詳細 (つまり、メソッド呼び出し) を完全に認識しています。クラスの内部が変更されるたびに、テストの更新に多くの非生産的な時間が費やされているのを目にします。
- 現時点では、私のサービス クラスがかなり単純化されているためかもしれませんが、これらのテストがどのような価値をもたらすかはよくわかりません。特定のユースケースが指示するように、コラボレートするオブジェクトが呼び出されることを保証しているということですか? コードの重複は、このようなわずかなメリットに対してとてつもなく高いようです。
私は何が欠けていますか?