A canonical Repository test
There are only so many ways to test that your persistence layer is implemented correctly or that you’re using an ORM correctly. Here’s my canonical tests for a repository (Java-version):
import static org.fest.assertions.api.Assertions.*;
public class PersonRepositoryTest {
private PersonRepository repository; // TODO < == you must initialize this
@Test
public void shouldSaveAllProperties() {
Person person = samplePerson();
repository.save(person); // TODO: Make sure your repository flushes!
assertThat(repository.find(person.getId())
.isNotSameAs(person)
.isEqualTo(person)
.isEqualsToByComparingFields(person);
}
@Test
public void shouldFindByCaseInsensitiveSubstringOfName() {
Person matching = samplePerson();
Person nonMatching = samplePerson();
matching.setName("A. Matching Person");
nonMatching.setName("A. Random Person");
repository.save(matching);
repository.save(nonMatching);
assertThat(repository.findByNameLike("MATCH"))
.contains(matching)
.doesNotContain(nonMatching);
}
}
Very simple. The samplePerson
test helper generates actually random people:
public class PersonTest {
// ....
public static Person samplePerson() {
Person person = new Person();
person.setName(sampleName());
// TODO Initialize all properties
return person;
}
public static String sampleName() {
return SampleData.randomWord() + " " + SampleData.randomWord() + "son";
}
}
public class SampleData {
public static String randomString() {
return random("foo", "bar", "baz", "qux", "quux"); // TODO: Add more!
}
public static <T> T random(T... options) {
return options[random(options.length)];
}
public static int random(int max) {
return random.nextInt(max);
}
private static Random random = new Random();
}
If your data has relationships with other entities, you may want to include those as well:
public class OrderRepositoryTest {
private OrderRepository repository; // TODO < == you must initialize this
private PersonRepository personRepository; // TODO <== you must initialize this
private Person person = PersonTest.samplePerson();
@Before
public void insertData() {
personRepository.save(person);
}
@Test
public void shouldSaveAllProperties() {
Order order = sampleOrder(person);
repository.save(order); // TODO: Make sure your repository flushes!
assertThat(repository.find(order.getId())
.isNotSameAs(order)
.isEqualTo(order)
.isEqualsToByComparingFields(order);
}
A simple and easy way to simplify your Repository testing. (The tests use FEST assert 2 for the syntax. Look at FluentAssertions for a similar API in .NET) (Yes, this is what some people would call an integration test. Personally, I can’t be bothered with this sort of classifications)
Comments:
Jakub Holý - Apr 26, 2013
Shouldn’t the test name be “shouldFindByName”? (“By” instead of “B”)
I am confued by setting the name to “A. Matching Person” but using “MATCH” in repository.findByNameLike(“MATCH”). I suppose that Like is case-insensitive substring match - I would prefere to have these two properties (substring, insensitive) mentioned either in the test’s name or at least in a comment. But perhaps in your team the meaning of Like is clear so this info isn’t needed.
BTW, I could not log-in with Twitter using FF, it just opens this blog in a new window. Ok in Chrome.
Johannes Brodwall - Apr 28, 2013
Thanks for the comment, Jakub. I’ve changed the name to shouldFindByCaseInsensitiveSubstringOfName
[Kasun Chaturanga Gajamange] - Jun 7, 2013
With the TDD we need to construct our code (or the solution ) with the series of tests. But if the application environment is heavy ( If applications loads hundreds of hibernate entities at the start-up) , I feel this tests makes some slowness on the coding. Am i right ? I felt this with a legacy application.. What are the practices to overcome this kind situations ..
Johannes Brodwall - Jun 7, 2013
Hi Kasun It was good to see you at the coding dojo tonight
The most important goal is to find a way to test the application without the slower parts like Hibernate. This can either be by faking/mocking the repository layer in most other tests, or by avoiding slow tech like Hibernate altogether. The latter is of course not always an option.
When mocking/faking the Repositories, you still need a set of test to run the cover the actual repository information (that’s the kind of tests I list here).