The "Object Mother"-Pattern
The "Object Mother"-Pattern is known to express in a succinct form what makes an object special.Let's assume we have a domain object Person with the two attributes name and surname.
class Person {
String _name, _surname;
public Person(String name, String surname) {
_name = name;
_surname = surname;
}
}
When we want to assure that a Person object with an empty surname is rejected by a PersonRepository when trying to save it, the simple solution would be the following:
@ExpectedException(ConstraintViolationException.class)
@Test public void shouldRejectPersonWithEmptySurname {
Person personWithEmptySurname = new Person("Name", "");
personRepository.save(personWithEmptySurname);
}
Though this is quite good (when using intent-revealing names), it becomes tedious when you want to test the behaviour in all possible attribute combinations. That's why the "Object Mother"-Pattern is handy. A possible ObjectMother for Person is:
class PersonMother {
Person _child = new Person("Name", "Surname");
PersonMother withEmptyName() {
_child._name = "";
return this;
}
PersonMother withEmptySurname() {
_child._surname = "";
return this;
}
Person build() {
return _child;
}
}
Using the PersonMother and a simple factory method we can simplify the test code:
@ExpectedException(ConstraintViolationException.class)
@Test public void shouldRejectPersonWithEmptySurname {
personRepository.save(aPerson().withEmptySurname().build());
}
PersonMother aPerson() {
return new PersonMother();
}
And the PersonMother simplifies the combination of attributes, too:
@ExpectedException(ConstraintViolationException.class)
@Test public void shouldRejectPersonWithEmptyNameAndSurname {
personRepository.save(
aPerson()
.withEmptyName()
.withEmptySurname().build());
}
Using the Object Mother for JMock expectations
After this excursus in the world of patterns, I am approaching my point, finally. Assuming that we want to test the behaviour of a service that depends on a PersonRepository to access persisted persons. In order to test the service in isolation, I mock the PersonRepository. I want to test the behaviour of the service depending on the contents of a returned Person. So we want to return different persons from the mocked repository. With "plain" JMock this results in the following setup code for our mock:PersonRepository mockPersonRepository = context.mock(PersonRepository.class);
context.checking(new Expectations() {{
oneOf(mockPersonRepository).get(1); returnValue(aPerson().withoutSurname().build());
}});
[...]
Not bad, actually. Very expressive and readable. But since I am quite a perfectionist, I am bothered by the required call to build(). So, let's see if we can simplify this further.
At first, I extract a generic interface from PersonMother:
interface ObjectMother<T> {
T build();
}
class PersonMother implements ObjectMother<Person> {
[...]
}
Afterwards, I create a new method to use in JMock expectations:
static Action returns(ObjectMother<?> objectMother) {
return new ReturnValueAction(objectMother.build());
}
With these two ingredients I am able to reduce the mock setup code as I wanted:
PersonRepository mockPersonRepository = context.mock(PersonRepository.class);
context.checking(new Expectations() {{
oneOf(mockPersonRepository).get(1); returns(aPerson().withoutSurname());
}});
[...]
Keine Kommentare:
Kommentar veröffentlichen