使用RhinoMocks进行unit testing和模拟

我正在尝试为我的新项目设置测试并遇到一些困难。

我正在使用NUnit和Rhino Mocks。

我试图测试的代码是这样的,

public DocumentDto SaveDocument(DocumentDto documentDto) { Document document = null; using (_documentRepository.DbContext.BeginTransaction()) { try { if (documentDto.IsDirty) { if (documentDto.Id == 0) { document = CreateNewDocument(documentDto); } else if (documentDto.Id > 0) { document = ChangeExistingDocument(documentDto); } document = _documentRepository.SaveOrUpdate(document); _documentRepository.DbContext.CommitChanges(); } } catch { _documentRepository.DbContext.RollbackTransaction(); throw; } } return MapperFactory.GetDocumentDto(document); } 

我的测试代码如下

 [Test] public void SaveDocumentsWithNewDocumentWillReturnTheSame() { //Arrange IDocumentService documentService = new DocumentService(_ducumentMockRepository, _identityOfSealMockRepository, _customsOfficeOfTransitMockRepository, _accountMockRepository, _documentGuaranteeMockRepository, _guaranteeMockRepository, _goodsPositionMockRepository); var documentDto = new NctsDepartureNoDto(); documentDto.IsDirty = true; documentDto.Id = 0; //Act var retDocumentDto = documentService.SaveDocument(documentDto); //Assert Assert.AreEqual(documentDto, documentDto); } private static IDbContext CreateMockDbContext() { var dbContext = MockRepository.GenerateMock(); // setup expectations for DbContext mock //dbContextMock.Expect(...) // bind mock of the DbContext to property of repository.DbContext _ducumentMockRepository.Expect(mock => mock.DbContext).Return(dbContext).Repeat.Any(); return dbContext; } 

我需要传入一个documentDto,并说出isDirty set并测试它是否返回相同的对象。

所以我想使用Stub而不是模拟。

我需要找出如何设置期望,以便我可以测试代码上的逻辑。

你需要模拟或存根你不想测试的所有组件。 根据经验,您应该最多只有一个模拟对象,其余应该是存根。 模拟您想要validation交互的内容,并存储您想要为测试提供数据的内容。

你没有告诉我们你的_documentRepository是什么类型,所以很难确切地说明你在这里测试的是什么,但是要测试这个方法你唯一可以做的事情,恕我直言,检查是否设置了IsDirty标志是检查调用_documentRepository和Context上的正确方法。

为此,我将创建一个模拟_documentRepository和模拟DbContext并设置期望_documentRepository.SaveOrUpdate(document)在传入_documentRepository.SaveOrUpdate(document)调用。实际上再次查看需要在dto和文档之间进行转换的代码。 目前,这是在一种方法中完成的。 我将为此创建一个接口和一个类,并使该接口成为您正在测试的类的依赖项,以便您可以创建一个存根,该存根从documentDto返回一个已知文档。 该类可以处理创建新文档或根据Dto中的id返回现有文档。 否则你将不得不知道返回什么类型的文件。

就像是:

 var documentDto = new NctsDepartureNoDto(); documentDto.IsDirty = true; documentDto.Id = 0; IDbContext context = MockRepository.GenerateMock(); context.Expect(x=>x.BeginTransaction()).Return(MockRepository.GenerateStub()); context.Expect(x=>x.CommitChanges()); 

然后为存储库创建一个模拟器

 IDocumentRepository repo = MockRepository.GenerateMock(); repo.Expect(x=>x.DbContext).Return(context).Repeat().Any(); repo.Expect(x=>x.SaveOrUpdate(Arg.Is.Any())).Return(MockRepository.GenerateStub); 

这将测试您在设置脏标志时是否正确地与存储库对象进行交互。 它不应该测试文档是否正确保存或者在调用SaveOrUpdate时返回正确的文档,因为这应该在存储库的测试中进行测试,而不是在这里。

‘可是等等!’ 我听到你哭了,’你刚开始说,应该只有一个模拟器,而这里我们有2个!’。 这是真的,我认为这显示了您当前设计的错误。

我不认为你不应该从documentRepository中公开DBContext。 您似乎这样做是为了使用交易。

如果您的存储库需要知道事务,那么存储库中的方法允许控制事务(或隐藏事务存储库对象内部完全存在的事实)。 这些方法可能只是委托给内部DbContext,但它意味着唯一的mock需要是文档存储库对象本身,而不是DbContext