Can I have real Mock using Rhino Mocks 3.6 without defining fake values?
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
orabstract
, or that your mock is based off aninterface
- 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);
}
精彩评论