我可以在Rhino-Mocks 3.6中用AAA语法测试方法调用顺序吗?

本文关键字:调用 测试方法 顺序 语法 AAA Rhino-Mocks 中用 我可以 | 更新日期: 2023-09-27 18:19:06

如果在Rhino-mocks 3.6中使用AAA语法调用Method1,然后调用Method2,然后调用Method3,是否可以测试以下示例?

// Assert
var mock = MockRepository.GenerateMock<ISomeService>();
// Act
myObject.Service = mock;
// How should I change this part to ensure that Rhino Mocks check the call order as well?
mock.AssertWasCalled(m=>m.Method1());
mock.AssertWasCalled(m=>m.Method2());
mock.AssertWasCalled(m=>m.Method3());

我可以在Rhino-Mocks 3.6中用AAA语法测试方法调用顺序吗?

有一种方法…

mock.AssertWasCalled(m=>m.Method1(), options => options.WhenCalled(w => mockService.AssertWasNotCalled(x=>x.Method2())));
mock.AssertWasCalled(m=>m.Method2(), options => options.WhenCalled(w => mockService.AssertWasNotCalled(x=>x.Method3())));
mock.AssertWasCalled(m=>m.Method3());

你可以,但你真的不应该。你应该把重点放在测试外部可观察的行为上,而不是测试实现。

方法调用顺序可以改变而不影响与API客户端的契约。在这种情况下,你的测试将会失败,即使它不应该失败。

简而言之,测试实现导致脆弱的测试。脆性测试导致放弃测试。你不会想去那里的。

如何做得更好

var mocks = new MockRepository();
var fooMock = mocks.DynamicMock<IFoo>();
using (mocks.Ordered())
{
    fooMock.Expect(x => x.Method1());
    fooMock.Expect(x => x.Method2());
}
fooMock.Replay();
var bar = new Bar(fooMock);
bar.DoWork();
fooMock.VerifyAllExpectations();

从这篇博文中找到了答案:验证方法执行顺序与Rhino mock 3.5 by Philip Japikse

如何通过在每个方法调用中构建断言来做到这一点。

// Arrange - Build the necessary assertions into the stubbed method invocations.
var mock = MockRepository.GenerateMock<ISomeService>();
mock.Stub(m => m.Method1()).WhenCalled(inv => mock.AssertWasNotCalled(m => m.Method2()));
mock.Stub(m => m.Method2()).WhenCalled(inv => mock.AssertWasNotCalled(m => m.Method3()));
// Act
myObject.Service = mock;
// Assert - Ensure each expected method was called.
mock.AssertWasCalled(m => m.Method1());
mock.AssertWasCalled(m => m.Method2());
mock.AssertWasCalled(m => m.Method3());

由于这是通过在执行过程中运行断言而混淆了正常的arrange-act-assert模式,因此我喜欢为这些实例包含非常具体的错误消息,以便更容易地识别测试失败。

mock.Stub(m => m.Method1()).WhenCalled(inv =>
    mock.AssertWasNotCalled(m => m.Method2(), opt =>
        opt.Message("Method2 cannot be called before Method1.")));

您还可以通过在行为步骤中将每次调用的结果保存在一个变量中,然后在断言步骤中检查变量状态来实现类似的结果。这更好地保留了安排-行为-断言模式的划分,但是编写和维护的管道代码更多。

// Arrange - Build the necessary state variables into the stubbed method invocations.
bool wasMethod1Called;
bool wasMethod2Called;
bool wasMethod2CalledBeforeMethod1;
bool wasMethod3CalledBeforeMethod2;
var mock = MockRepository.GenerateMock<ISomeService>();
mock.Stub(m => m.Method1()).WhenCalled(inv =>
{
    wasMethod1Called = true;
});
mock.Stub(m => m.Method2()).WhenCalled(inv =>
{
    wasMethod2Called = true;
    wasMethod2CalledBeforeMethod1 = !wasMethod1Called;
});
mock.Stub(m => m.Method3()).WhenCalled(inv =>
{
    wasMethod3CalledBeforeMethod2 = !wasMethod2Called;
});
// Act
myObject.Service = mock;
// Assert - Ensure each expected method was called, and that they were called in the right order.
mock.AssertWasCalled(m => m.Method1());
mock.AssertWasCalled(m => m.Method2());
mock.AssertWasCalled(m => m.Method3());
Assert.That(wasMethod2CalledBeforeMethod1, Is.False, "Method2 cannot be called before Method1.");
Assert.That(wasMethod3CalledBeforeMethod2, Is.False, "Method3 cannot be called before Method2.");

@craastad指定的mocks.Ordered()语法是正确的方法,但是我无法让它在RhinoMocks 3.5中工作-相反,我不得不调整它以使其在没有MockRepository实例的情况下工作,@craastad的解决方案用于调用Ordered():

var fooMock = MockRepository.GenerateMock<IFoo>();
using (fooMock.GetMockRepository().Ordered())
{
    fooMock.Expect(x => x.Method1());
    fooMock.Expect(x => x.Method2());
}
var bar = new Bar(fooMock);
bar.DoWork();
fooMock.VerifyAllExpectations();

如果这样做,似乎也没有必要调用fooomock . replay()。