单元测试和嘲讽使用RhinoMocks单元测试、RhinoMocks

2023-09-03 07:12:05 作者:桃色情愫

我想安装测试我的新项目,并遇到了一些困难。

I am trying to setup tests for my new projects and come across some difficulties.

我使用NUnit和犀牛制品。

I am using NUnit and Rhino Mocks.

在code,我想测试是这样的,

The Code that I am trying to test is this,

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);
}

和我的测试code如下:

And my testing code is as follows

[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<IDbContext>();

    // 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设置和测试它是否返回相同的对象。

I need to pass in a documentDto with say isDirty set and test if it returns the same object.

所以,我想用一个模拟的存根不是。

So I was thinking to use a Stub instead of a mock.

我需要找出如何设置的期望,所以我可以测试在code中的逻辑。

I need to to find out how to set expectations so I can test the logic on the code.

推荐答案

您需要模拟或存根所有你不想要测试的组件。你应该,作为一个经验法则最多只有单一的模仿对象的其余部分应存根。嘲笑你想验证与互动的东西和存根,你只是想提供数据测试的东西。

you need to mock or stub all of the components which you do not want to test. You should, as a rule of thumb only have a maximum of single mock object the rest should be stubs. Mock the things you want to verify interaction with and stub the things which you just want to provide data for your test.

你不告诉我们是什么类型的_documentRepository那么它很难告诉你在这里测试什么,而是为了测试这个方法,你唯一可以做的事情,恕我直言,是检查是否IsDirty标志被设置为检查的_documentRepository和语境正确的方法被调用。

you don't tell us what type your _documentRepository is so its hard to tell exactly what you are testing here, but to test this method the only thing you can do, IMHO, is check that if the IsDirty flag is set is check that the correct methods on the _documentRepository and the Context are called.

要做到这一点,我将创建一个模拟_documentRepository和模拟的DbContext,并设置期望 _documentRepository.SaveOrUpdate(文件)调用时传入的文件,其实再看在code,你需要的DTO和文档之间的转换。目前,这个正在做的方法。我想创建这样的接口和类,使该接口的类要测试,让你可以创建一个存根返回从documentDto已知文件的依赖。这个类可以处理创造一个新的文档或返回基于在DTO的ID已经存在。否则,你就必须知道返回什么类型的文件。

To do this I would create a mock _documentRepository and mock DbContext and set expectations that _documentRepository.SaveOrUpdate(document) is called with the document passed in. Actually looking again at the code you need to convert between the dto and the document. Currently this is being done in a method. I would create a interface and a class for this and make that interface a dependency of the class you are testing so that you can create a stub which returns a known document from the documentDto. This class could handle creating a new document or returning an existing one based on the id in the Dto. otherwise you'll have to know what type of document is returned.

是这样的:

var documentDto = new NctsDepartureNoDto();
documentDto.IsDirty = true;
documentDto.Id = 0;

IDbContext context = MockRepository.GenerateMock<IDbRepository>();  
context.Expect(x=>x.BeginTransaction()).Return(MockRepository.GenerateStub<ITransaction>());
context.Expect(x=>x.CommitChanges());

然后创建一个模拟存储库

then create a mock for the repository

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

这是你的库对象正确交互时脏标志设置此测试。它不应该测试该文件是否正确,或当 SaveOrUpdate 被称为正确的文档返回,因为这应该在测试存储库进行测试,而不是保存在这里

This tests that you interact with the repository object correctly when the dirty flag is set. It shouldn't test that the document is saved correctly or that the correct document is returned when SaveOrUpdate is called, as this should be tested in the tests for the repository, not here.

别急!我听见你哭了,你一开始说,应该只有一个单一的模拟,在这里,我们有2个!。这是真的,我认为,这表明在当前的设计缺陷。

'But wait!' I hear you cry, 'you said at the beginning that there should only be a single mock, and here we have 2!'. That's true, and I think that this shows a fault in your current design.

您应该不,我不认为,从您的DocumentRepository露出的DbContext。你似乎为了使用事务被这样做。

You should not, I don't think, be exposing the DBContext from your documentRepository. You seem to be doing so in order to use the transactions.

如果你的资料​​库需要了解交易的话对,允许交易进行控制(或者隐瞒事实,该库对象内部存在完全的事务)资源库的方法。这些方法可能只是委托给内部的DbContext,但它会那么意味着只有模拟将需要的文件存放对象本身,而不是的DbContext

If your repository needs to be aware of the transactions then have methods on the repository that allow the transactions to be controlled (or hide the fact that the transactions exist inside the repository object completely). These methods might just delegate to the internal DbContext but it would then mean that the only mock would need to be the document repository object itself, and not the DbContext