Integration Testing with Spring – Data III

Gil Zilberfeld explains the data integration tests in Spring
Standard
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

Hibernate and JPA have done a lot for developers in minimizing the code needed to write database applications. Spring Data does a lot of magic behind the code for us, and we’d like to take advantage of that magic.

Let’s start with the basics: In JPA we’re mapping objects to tables in the database, or rather, inform Spring what tables we want for the objects.

So if our object is like this:

@Entity
@Table(name = "Products")
public class Product {

    @Id
    @GeneratedValue
    private int id;

    @Size(min = 3, max = 20)
    private String name;

    @Size(min = 3, max = 20)
    private String countryOfOrigin;

    @Override
    public String toString() {
    return String.format("%s from %s",
    		this.name, this.countryOfOrigin);
    }

    ...

}

Spring understands we want a table called Products, where each row is mapped to a Product instance. The ID is the index of the table, and it’s going to be auto-generated. But Spring needs more information – we need a repository object to access that data:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>  {
}

Doesn’t look like much, right? But this empty interface does a lot. JpaRepository is full of data access methods, and with Spring magic, it creates all the code needed for us to use these objects as if we’ve created them and the database manually.

We also need to configure the data source, which can be configured either through application.properties file or Spring’s DataSource APIs.

The main @Bean we need to configure in our configuration class is the LocalContainerEntityManagerFactoryBean, which does all the connections, loading and initialization for our database. That includes where to find repository classes, and setting the data source. I’m not going in depth here, since I want to move on to integration testing features.

Test, Please

Spring Boot comes with a handy @DataJpaTest annotation that extends @SpringBootTest. It tells Spring that our integration test is not just a regular integration test, but requires help from the Spring JPA gods, and it comes in the shape of a bean called TestEntityManager.

@RunWith(SpringRunner.class)
@ContextConfiguration(classes= {DataJpaConfiguration.class })
@DataJpaTest
public class ProductControllerTests {

	@Autowired TestEntityManager entityManager;

	...
}

Spring creates this object for us, no need for configuration, so we can us it in our integration tests. What for, you ask? Remember we had the issue of checking the result of the added item in the database we had with JDBC? Our TestEntityManager is the JdbcTemplate counterpart. It helps us put stuff in the database, or help us query it.

(You may also notice we didn’t use the @SQL annotation this time to build the database, because Spring builds the table for us, by identifying the @Entity annotation on our class.)

Now we can write the integration test:

@Test
public void twoProductsRetrieved_afterAddingTwo() {
	Product product1 = new Product("Brie", "France");
	Product product2 = new Product("Parmigiano", "Italy");

	entityManager.persist(product1);
	entityManager.persist(product2);
	entityManager.flush();

	// controller queries the JPA repository internally
	String productDescription = controller.getAllProductDescriptions();

	assertEquals("{Brie from France,Parmigiano from Italy}",
			productDescription);
}

We use the TestEntityManager to push data into the database, and then check that the data was returned correctly from the controller.

Data integration tests are very useful in terms of making sure that our code runs correctly up to the database. Spring, with a wee help from in-memory databases, helps solve a lot of issues of isolation and setup, as well as focusing on writing what we need in the integration tests. Cool stuff.

So far we’ve checked classes we’ve created and invoked. Next we’re going to go a step up: Spring Controllers and how to test them.

Leave a Reply

Your email address will not be published. Required fields are marked *