开发者

Can I have real Mock using Rhino Mocks 3.6 without defining fake values?

开发者 https://www.devze.com 2023-02-19 21:35 出处:网络
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 wha

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 "Mo开发者_StackOverflow中文版nica" and have a real mock object for Person?

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


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


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号