我可以使用Rhino Mocks 3.6进行真正的Mock而不定义假值吗?

Jun*_*r M 0 c# rhino-mocks mocking

我正在使用Rhino Mocks 3.6.我见过很多类型的编码.有时使用静态GenerateMock方法,有时使用new MockRepository().我不太了解发生了什么或什么更好.也许有些方法已经过时,但无论如何,让我们回到真正的问题.

我想更好地了解下面的代码中发生了什么,以及更好的测试需要什么.

    [TestMethod]
    public void TestingProperty()
    {
        Person repository = MockRepository.GenerateMock<Person>();
        // tell rhino.mocks when FirstName is called in the next time, it should return "Monica"
        repository.Expect(x => x.Title).Return("Monica");
        // get the mocking ready
        repository.Replay();

        repository.VerifyAllExpectations();

        // when getting repository.Title value, we should receive "Monica" defined previously in expectation
        Assert.AreEqual(repository.Title, "Monica");
    }
Run Code Online (Sandbox Code Playgroud)

我注意到当我移除时repository.Replay(),一切都在继续工作.重播的目的是什么,是否需要?

还需要VerifyAllExpectations吗?它在内部做什么?

我可以避免手动输入"Monica"并为Person设置一个真实的模拟对象吗?

如果这是一个错误的代码,请告诉我你的建议!

Mer*_*ham 5

听起来你没有使用过更新的模拟对象框架.

在较旧的"模拟对象"中,您必须手动对模拟对象的状态进行断言.例如,您将运行测试下的代码,该代码将项添加到模拟对象的列表中.在测试结束时,您将验证是否正确填充了该模拟对象上的列表.这是验证模拟的状态.

这种较旧的风格就像是Rhino存根的不太复杂的版本.

使用较新的模拟对象框架,您将停止验证模拟对象的状态,并开始验证行为.您对测试中的代码如何调用模拟对象进行断言,而不是如何设置属性/成员.

你仍然会对被测代码做经典断言.但是你不会对你的模拟做经典断言.您将设置期望,并使用Rhino断言验证它们.

一些提示,以纠正此代码:

  • 这里使用了Rhino Mocks API的两个部分.删除记录/回放部分,因为它更令人困惑,并坚持使用AAA(Arrange,Act,Assert)语法.请参阅添加AAA时的发行说明
  • 停止直接测试模拟对象
  • 测试使用模拟对象的代码,并添加对该代码将在模拟上调用的内容的期望
  • 在调用之前调用您的代码VerifyAllExepectations,并在必要时将mocks传递给它
  • 确保您想嘲笑的方法和属性被标记virtual或者abstract,或者你的模拟是基于一个关闭interface
  • 将测试用例拆分为用于验证调用mocks的测试,以及验证状态/返回值在测试代码中是否正确的测试
  • 根本不要在模拟上声明状态,因为你直接设置它,并且只是测试你的测试用例

这是一些更正的示例代码:

public class ObjectThatUsesPerson
{
    public ObjectThatUsesPerson(Person person)
    {
        this.person = person;
    }

    public string SomeMethod()
    {
        return person.Title;
    }

    private Person person;
}

[TestMethod]
public void TestingPropertyGotCalled()
{
    // Arrange
    var mockPerson = MockRepository.GenerateMock<Person>();
    mockPerson.Expect(x => x.Title).Return("Monica");
    var someObject = new ObjectThatUsesPerson(mockPerson);

    // Act
    someObject.SomeMethod(); // This internally calls Person.Title

    // Assert
    repository.VerifyAllExpectations();
    // or: mockPerson.AssertWasCalled(x => x.Title);
}

[TestMethod]
public void TestingMethodResult()
{
    // Arrange
    var stubPerson = MockRepository.GenerateStub<Person>();
    stubPerson.Stub(x => x.Title).Return("Monica");
    var someObject = new ObjectThatUsesPerson(stubPerson);

    // Act
    string result = someObject.SomeMethod();

    // Assert
    Assert.AreEqual("Monica", result, "Expected SomeMethod to return correct value");
}
Run Code Online (Sandbox Code Playgroud)

要测试这是否正常,请尝试这些(在每个之后更改代码):

  • 运行一次,确保它通过
  • 删除该Expect行,运行它,并确保它仍然通过(这不是一个严格的模拟)
  • 添加额外的Expect行,返回不同的值.运行它,并确保它失败
  • 添加额外的SomeMethod电话.运行它,并确保它通过(不是严格的模拟)
  • 删除里面的代码SomeMethod.运行它,并确保它失败