在使用Rhino模拟运行测试之前,如何测试需要调用以设置对象的方法?

时间:2009-08-21 10:38:40

标签: unit-testing rhino-mocks mvp

我在测试这种情况时遇到了问题。

发票有两种状态 - 已完成和未完成 - 我想测试方法Presenter.FinishInvoice()调用DAO.FinishInvoice()然后调用DAO.GetInvoice()然后使用结果设置View.Invoice。问题是我需要调用DAO.GetInvoice()来获取发票以便在第一时间完成,这是从Presenter.InitializeView()调用的(在另一个测试中测试)。

这是我的测试:

using (mocks.Record())
{
    SetupResult.For(view.Invoice).PropertyBehavior();
    SetupResult.For(DAO.GetInvoice(1)).Return(invoice);
    Expect.Call(DAO.FinishInvoice(1)).Return(true);
    Expect.Call(DAO.GetInvoice(1)).Return(invoice);
}
using (mocks.Playback())
{
    Presenter presenter = new Presenter(view, DAO);
    presenter.InitializeView(1);
    presenter.FinishInvoice();
}

调用InitializeView()时,将调用DAO.GetInvoice()并设置一次View.Invoice。它不是测试的一部分,但如果我没有将View.Invoice设置为未完成的发票,则FinishInvoice()将失败,因此需要设置返回值。

第二次调用DAO.GetInvoice()从FinishInvoice()调用,测试的一部分。

如果我运行此测试,我会在DAO.GetInvoice(1)上失败;期望#1,实际#0。我已经逐步完成了代码,并在调用FinishInvoice()时调用DAO.GetInvoice(),因此它必须是我的测试代码有问题,而不是我的演示者代码。

如果我改变:

    SetupResult.For(DAO.GetInvoice(1)).Return(invoice);

为:

    Expect.Call(DAO.GetInvoice(1)).Return(invoice);

它有效,但不应该是测试的一部分,因为它只是设置所需(但不能放入SetUp方法,因为它不是所有测试都需要的)

我认为使用Expect.Call()并不是我需要做的灾难,但是我想学习如何设置它我想要它的方式。

1 个答案:

答案 0 :(得分:0)

由于您要测试DAO级别的交互,因此需要创建它as a mock and not as a stub。这意味着您无法使用SetupResult。

如果您不关心方法调用的顺序,可以使用Repeat-syntax

using (mocks.Record())
{
    SetupResult.For(view.Invoice).PropertyBehavior();
    Expect.Call(DAO.FinishInvoice(1)).Return(true);
    Expect.Call(DAO.GetInvoice(1)).Return(invoice).Repeat.Any();
}
using (mocks.Playback())
{
    Presenter presenter = new Presenter(view, DAO);
    presenter.InitializeView(1);
    presenter.FinishInvoice();
}

如果您关心方法调用的顺序,则必须使用Ordered-syntax明确指定每个期望:

using (mocks.Record())
{     
    SetupResult.For(view.Invoice).PropertyBehavior();

    using (mocks.Ordered())
    {  
       Expect.Call(DAO.GetInvoice(1)).Return(invoice);     
       Expect.Call(DAO.FinishInvoice(1)).Return(true);
       Expect.Call(DAO.GetInvoice(1)).Return(invoice);
    }
}
using (mocks.Playback())
{
    Presenter presenter = new Presenter(view, DAO);
    presenter.InitializeView(1);
    presenter.FinishInvoice();
}

但是,如果您在yor代码中调用DAO.GetInvoice两次,我会说这是代码味道,您应该考虑将其重构为一次调用。

此外,以下是AAA-syntax from 3.5

的情况
//Arrange
DAO.Stub( x => x.GetInvoice(1) ).Return(true).Repeat.Any();

//Act
Presenter presenter = new Presenter(view, DAO);
presenter.InitializeView(1);
presenter.FinishInvoice();

//Assert
DAO.AssertWasCalled( x => x.FinishInvoice(1) );
DAO.AssertWasCalled( x=> x.GetInvoice(1) );

正如您所看到的那样,这样做更好,您甚至可以将模拟用作模拟和存根。