断言一个方法只被调用一次

时间:2008-11-13 02:32:54

标签: tdd mocking rhino-mocks assertions

我想声明一个方法只被调用一次。我正在使用RhinoMocks 3.5。

以下是我认为可行的方法:

[Test]
public void just_once()
{
    var key = "id_of_something";

    var source = MockRepository.GenerateStub<ISomeDataSource>();
    source.Expect(x => x.GetSomethingThatTakesALotOfResources(key))
        .Return(new Something())
        .Repeat.Once();

    var client = new Client(soure);

    // the first call I expect the client to use the source
    client.GetMeMyThing(key);

    // the second call the result should be cached
    // and source is not used
    client.GetMeMyThing(key);
}

如果GetMeMyThing()的第二次调用调用source.GetSomethingThatTakesALotOfResources(),我希望此测试失败。

7 个答案:

答案 0 :(得分:32)

以下是我验证方法被调用一次的方法。

[Test]
public void just_once()
{
    // Arrange (Important to GenerateMock not GenerateStub)
    var a = MockRepository.GenerateMock<ISomeDataSource>();
    a.Expect(x => x.GetSomethingThatTakesALotOfResources()).Return(new Something()).Repeat.Once();

    // Act
    // First invocation should call GetSomethingThatTakesALotOfResources
    a.GetMeMyThing();

    // Second invocation should return cached result
    a.GetMeMyThing();

    // Assert
    a.VerifyAllExpectations();
}

答案 1 :(得分:15)

我一直在使用AssertWasCalled扩展来解决这个问题。这是我能找到/得到的最好的,但如果我不必两次指定呼叫会更好。

    [Test]
    public void just_once()
    {
        var key = "id_of_something";

        var source = MockRepository.GenerateStub<ISomeDataSource>();

        // set a positive expectation
        source.Expect(x => x.GetSomethingThatTakesALotOfResources(key))
            .Return(new Something())
            .Repeat.Once();

        var client = new Client(soure);
        client.GetMeMyThing(key);
        client.GetMeMyThing(key);

        source.AssertWasCalled(x => x.GetSomethingThatTakesALotOfResources(key),
                               x => x.Repeat.Once());
        source.VerifyAllExpectations();
    }

答案 2 :(得分:5)

您可能对Rhino Mocks 3.5文档(引用如下)中的this bit感兴趣。看起来你需要模拟类,而不是存根,因为它可以按照你期望的方式工作。

  

存根和模拟之间的区别

     

...

     

模拟是我们可以设置的对象   期望,并将验证   预期的行动确实存在   发生了。存根是您的对象   用于传递给代码   测试。你可以设定期望   它,所以它会以某种方式行动,   但这些期望永远不会   验证。存根的属性会   自动表现得像平常一样   属性,你不能设置   对他们的期望。

     

如果要验证其行为   在测试的代码中,您将使用一个   嘲笑有适当的期望,   并验证。如果你想要   传递可能需要在a中执行的值   某种方式,但不是焦点   这个测试,你将使用存根。

     

重要提示:存根永远不会导致   测试失败。

答案 3 :(得分:2)

以下是我刚刚做过的事情(按照Ray Houston的建议)。我仍然会欣赏更优雅的解决方案......

[Test]
public void just_once()
{
    var key = "id_of_something";

    var source = MockRepository.GenerateStub<ISomeDataSource>();

    // set a positive expectation
    source.Expect(x => x.GetSomethingThatTakesALotOfResources(key))
        .Return(new Something())
        .Repeat.Once();

    var client = new Client(soure);

    client.GetMeMyThing(key);

    // set a negative expectation
    source.Expect(x => x.GetSomethingThatTakesALotOfResources(key))
        .Return(new Something())
        .Repeat.Never();

    client.GetMeMyThing(key);
}

答案 4 :(得分:2)

您可以将委托传递给WhenCalled以计算来电:

...
uint callCount = 0;
source.Expect(x => x.GetSomethingThatTakesALotOfResources(key))
    .Return(new Something())
    .WhenCalled((y) => { callCount++; });
...
Assert.AreEqual(1, callCount);

此外,您应该使用模拟而不是存根,并验证对模拟的期望。

答案 5 :(得分:0)

如果要确保只调用一次方法,则可以创建严格模拟。

var mock = MockRepository.GenerateStrictMock<IMustOnlyBeCalledOnce>();
mock.Expect(a => a.Process()).Repeat.Once();
var helloWorld= new HelloWorld(mock);

helloworld.Process()

mock.VerifyAllExpectations();

答案 6 :(得分:-1)

拥有一个名为“Exactly”的功能,可以方便地编写可能会进入无限循环的代码测试。我很乐意编写一个测试,以便第二次调用方法会引发异常。

python的某些库允许您对期望进行排序,因此第一个返回false,第二个引发异常。

犀牛不会这样做。使用.Once的部分模拟将拦截第一个调用,其余部分将传递给原始方法。这太糟糕了,但这是真的。

你必须创建一个手工模拟。派生一个“可测试的”课程,并让它在第一次通话后加注。