Another reason for Rhino Mocks Generic Constraint 2

Posted by Aaron Feng Mon, 08 Oct 2007 12:22:00 GMT

Jeffrey Palermo recently had a post about Generic Constraints for Rhino Mocks - make unit tests more readable. I would like to touch on an alternative reason why you might want to use Generic Constraint. I'll reiterate Jeffrey's example before I start, with minor modification, and offer possible alternative ways the tests can be written.

Jeffrey implemented a GenericConstraint class to capture the parameter of the method call on a mock object. Below resembles his original example:

[Test]
public void ShouldSaveObjectWithAllInformation() {
    string firstName = "Aaron";
    string lastName = "Feng";

    MockRepository mocks = new MockRepository();
    IPersonRepository personRepository = mocks.CreateMock<IPersonRepository>();

    personRepository.SavePerson(null);
    GenericConstraint<Person> personConstraint = new GenericConstraint<Person>();
    LastCall.On(personRepository).Constraints(personConstraint);

    mocks.ReplayAll();

    PersonController controller = new PersonController(personRepository);
    controller.PersonFirstName = "Aaron";
    controller.PersonLastName = "Feng";
    controller.Save();

    mocks.VerifyAll();

    Person person = personConstraint.GetParameterObject();
    Assert.AreEqual(person.FirstName, firstName);
    Assert.AreEqual(person.LastName, lastName);
}

Once the PersonConstraint captured the Person that is being saved, he asserted that the values are as expected. This makes the test look more like a typical unit test.

Jeffrey's goal was to avoid the following code:

public delegate void Proc<P>(P p);

[Test]
public void ShouldSaveObjectWithAllInformationUsingBuildInConstraint() {
    string firstName = "Aaron";
    string lastName = "Feng";

    MockRepository mocks = new MockRepository();
    IPersonRepository personRepository = mocks.CreateMock<IPersonRepository>();

    personRepository.SavePerson(null);
    LastCall.On(personRepository).IgnoreArguments().Do(
        new Proc<Person>(delegate(Person person) {
            Assert.AreEqual(person.FirstName, firstName);
            Assert.AreEqual(person.LastName, lastName);
        })
    );

    mocks.ReplayAll();

    PersonController controller = new PersonController(personRepository);
    controller.PersonFirstName = "Aaron";
    controller.PersonLastName = "Feng";
    controller.Save();

    mocks.VerifyAll();
    // Notice no asserts
}

An astute reader might say: "Hey you don't have to do that, just implement the Equals method on the Person class." Which would look like the following:

[Test]
public void ShouldSaveObjectWithAllInformationUsingEquals() {
    MockRepository mocks = new MockRepository();
    IPersonRepository personRepository = mocks.CreateMock<IPersonRepository>();

    // Have to implement Equals on Person
    personRepository.SavePerson(new Person("Aaron", "Feng"));

    mocks.ReplayAll();

    PersonController controller = new PersonController(personRepository);
    controller.PersonFirstName = "Aaron";
    controller.PersonLastName = "Feng";
    controller.Save();

    mocks.VerifyAll();
    // Notice no asserts again
}

The last example by implementing an Equals on the Person object which made the test look too clean. The asserts are invisible. On top of that, you have to implement an Equals method on an Object which you might not ever call the Equals in the real system. I believe this is the real power behind Jeffrey's Generic constraint approach. One should avoid writing any code that is not utilized by the real system just to satify the test.

Comments

Leave a response

  1. Bjorn Reppen Sun, 06 Jan 2008 19:38:59 GMT

    I wonder if it's even possible for developers who are unfamiliar with the framework to actually read and understand the topmost example? I tried, but gave up after about a minute. And you dare call this more readable?

    Come on guys, what happened to KISS?

  2. Aaron Feng Sun, 06 Jan 2008 23:53:09 GMT

    The readability comes after understanding the framework used here (RhinoMocks and NUnit).

    Look at the second example, it's even more cryptic, and third it's magical.

    If you are having trouble understanding the example, perhaps I can explain it in a different way. Just clarify what parts you are confused about in an email. My contact info can be found on the side bar.

Comments