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

发布于 2024-10-26 17:24:58 字数 979 浏览 0 评论 0原文

我正在使用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");
    }

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

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

我可以避免手动输入“Monica”并为 Person 提供一个真正的模拟对象吗?

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

I am using Rhino Mocks 3.6. I have seen many types of coding. Sometimes using static GenerateMock method, sometimes using new MockRepository(). I don't understand pretty well what is happening or what is better. Maybe some methods are obsolete, but anyway, let's go to the real issue.

I would like to understand better what is happening in the code below and what is really needed to have a better test.

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

I noticed when I remove repository.Replay(), everything keeps on working. What is the purpose of Replay, is it needed?

VerifyAllExpectations is also needed? What is it doing internally?

Can I avoid typing manually "Monica" and have a real mock object for Person?

If this is a bad code please let me know your suggestions!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

樱娆 2024-11-02 17:24:58

听起来您还没有使用过更新的模拟对象框架。

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

这种较旧的样式就像犀牛存根的不太复杂的版本。

使用较新的模拟对象框架,您可以停止验证模拟对象的状态,并开始验证行为。您对被测代码如何调用模拟对象做出断言,而不是如何设置属性/成员。

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

纠正此代码的一些提示:

  • 这里使用了 Rhino Mocks API 的两个部分。删除记录/播放部分,因为它更容易混淆,并坚持 AAA(Arrange、Act、Assert)语法。请参阅发行说明从添加 AAA 时开始
  • 停止直接测试模拟对象
  • 测试使用模拟对象的代码,并添加对代码将在模拟上调用的内容的期望 调用
  • 您的在调用 VerifyAllExepectations 之前测试代码,根据需要向其传递模拟
  • 确保要模拟的方法和属性标记为 virtualabstract,或者您的模拟基于接口
  • 将您的测试用例拆分为验证模拟被调用的测试,以及验证被测代码的状态/返回值是否正确的测试
  • 不要断言状态在模拟上,因为您直接设置它,并且只是测试您的测试用例

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

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

要测试它是否正常工作,请尝试这些操作(在每一个之后将代码更改回来):

  • 运行一次并进行确保它通过
  • 删除 Expect 行,运行它,并确保它仍然通过(这不是严格的模拟)
  • 添加额外的 Expect 行,返回不同的值。运行它,并确保它失败
  • 添加额外的 SomeMethod 调用。运行它,并确保它通过(不是严格的模拟)
  • 删除 SomeMethod 中的代码。运行它,并确保它失败

It sounds like you haven't worked with more recent mock object frameworks.

In older "mock objects", you have to do assertions manually against state of the mock object. For example, you'd run the code under test, which adds items to a list on your mock object. At the end of your test, you'd verify that the list on that mock object is populated correctly. This is verifying the state of the mock.

This older style is like a less sophisticated version of a Rhino stub.

With newer mock object frameworks, you stop verifying state of mock objects, and start verifying behavior. You make assertions about how your code under test calls your mock objects, not how properties/members are set.

You'll still do your classical assertions on the code under test. But you won't do classical assertions on your mocks. You'll instead set up expectations, and verify them with Rhino assertions.

Some tips to correct this code:

  • There are two parts of the Rhino Mocks API being used here. Drop the record/playback portion, since it is more confusing, and stick to the AAA (Arrange, Act, Assert) syntax. See the release notes from when AAA was added
  • Stop testing mock objects directly
  • Test code that uses the mock object, and add expectations for what that code will call on the mock
  • Call your code under test before the call to VerifyAllExepectations, passing mocks to it as necessary
  • Ensure that the methods and properties you want to mock are marked virtual or abstract, or that your mock is based off an interface
  • Split your test cases into tests that verify mocks get called, and tests that verify state/return values are correct on the code under test
  • Don't assert state on mocks at all, since you directly set it, and that just tests your test case

Here's some corrected example code:

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

To test that this is working correctly, try these things (change the code back after each one):

  • Run it once and make sure it passes
  • Remove the Expect line, run it, and make sure it still passes (this isn't a strict mock)
  • Add an extra Expect line, returning a different value. Run it, and make sure it fails
  • Add an extra SomeMethod call. Run it, and make sure it passes (not a strict mock)
  • Remove the code inside SomeMethod. Run it, and make sure it fails
予囚 2024-11-02 17:24:58

使用 RhinoMocks 编写测试有 2 种常见方法 - 带有期望的模拟对象以及 Arrange、Act、Assert (AAA)。您应该阅读 http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx< /a> 文章非常详细地介绍了它。

Merlyn Morgan-Graham 的回答涵盖了第一种方法。以下是如何使用 AAA 模型编写相同的测试:

[TestMethod]
public void TestingPropertyUsingAAA()
{   
   // Arrange
   var mockPerson = MockRepository.GenerateStub<Person>();
   repository.Stub(x => x.Title).Return("Monica");

   // Act
   var someObject = new ObjectThatUsesPerson(mockPerson);
   someObject.SomeMethod(); // This internally calls Person.Title    

   // Assert
   repository.AssertWasCalled(x => x.Title);
}

There are 2 common approaches to writing tests with RhinoMocks - mock objects with expectations and Arrange, Act, Assert (AAA). You should read http://ayende.com/Wiki/Rhino+Mocks+3.5.ashx article that covers it in great detail.

Merlyn Morgan-Graham answer covers first approach. Follwing is how you can write the same test using AAA model:

[TestMethod]
public void TestingPropertyUsingAAA()
{   
   // Arrange
   var mockPerson = MockRepository.GenerateStub<Person>();
   repository.Stub(x => x.Title).Return("Monica");

   // Act
   var someObject = new ObjectThatUsesPerson(mockPerson);
   someObject.SomeMethod(); // This internally calls Person.Title    

   // Assert
   repository.AssertWasCalled(x => x.Title);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文