Last Updated on 10 June 2020   |   Print Email
Through this tutorial, you will learn how to write unit tests for a Spring Boot project that uses Spring Data JPA and Hibernate for the data access layer. I will also share with you how I write code for testing CRUD operations of a Spring Data JPA repository.I will be using JUnit 5 (JUnit Jupiter) in a Spring Boot project with Spring Data JPA, Hibernate and MySQL database.
1. Annotations for Unit Testing Spring Data JPA
When coding the data access layer, you can test only the Spring Data JPA repositories without testing a fully loaded Spring Boot application.Spring Boot provides the following annotations which you can use for unit testing JPA repositories:
@DataJpaTest:
This annotation will disable full auto-configuration and instead apply only configuration relevant to JPA tests. By default, it will use an embedded, in-memory H2 database instead of the one declared in the configuration file, for faster test running time as compared to disk file database.Let’s see an example test class:
As you can see, in this test class we can inject a TestEntityManagerand ProductRepository. TestEntityManager is a subset of JPA EntityManager. It allows us to quickly test JPA without the need to manually configure/instantiating an EntityManagerFactory and EntityManager.
And ProductRepository is the repository that need to be tested, along with the entity class Product. The repository proxy class generated by Spring Data JPA is very well implemented, so it’s for testing the entity classes and custom methods in repository interfaces.You also see I use assertion method from AssertJ for more readable, meaningful assertion statements – instead of JUnit’s ones:
Note that by default, tests using @DataJpaTest are transactional and roll back at the end of each test method. If you want to disable auto rollback for the whole test class, annotate the class with the @Rollback annotation as follows:
@DataJpaTest
@Rollback(false)
public class ProductRepositoryTests {
...
}
Or you can selectively disable roll back at method level. For example:
@DataJpaTest
public class ProductRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private ProductRepository repository;
@Test
@Rollback(false)
public void testSaveNewProduct() {
...
}
@Test
public void testUpdateProduct() {
...
}
}
Disabling roll back for tests will be useful when a test method depends on the data of others.
@AutoConfigureTestDatabase:
By default, the @DataJpaTest annotation replaces the declared database configuration by an in-memory database (H2), which is useful when running tests that doesn’t touch real database. When you want to run tests on real database, use the @AutoConfigureTestDatabase as follows:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class ProductRepositoryTests {
...
}
Then Spring Boot will use the data source declared in the application configuration file.Next, let’s go through a sample Spring Boot project that uses unit tests for Spring Data JPA.
2. Required Dependencies
If you create a Spring Boot project using Spring Tool Suite IDE or directly from Spring Initializr, the dependency spring boot starter test is included by default. And we need to add dependencies for the in-memory database (H2) and real database (MySQL).So make sure to declare the following dependencies in the Maven’s project file:
Note that we exclude JUnit Vintage which supports running JUnit 4’s tests. Since we will write tests using JUnit 5, JUnit Vintage is no needed.
3. Code Entity Class
Create an entity class named Product with the following code:
package net.codejava;
import javax.persistence.*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 64, unique = true, nullable = false)
private String name;
private float price;
public Product(String name, float price) {
this.name = name;
this.price = price;
}
public Product() {
}
// getters and setters are not shown for brevity...
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
As you can see, this is a simple entity class with only 3 fields: id, name and price. The getters and setters are not shown for brevity. We will use Hibernate forward engineering to create the corresponding table in the database when running tests.
4. Code Repository Interface
Because we use Spring Data JPA with Hibernate, so code the ProductRepository interface as follows:
package net.codejava;
import org.springframework.data.repository.CrudRepository;
public interface ProductRepository extends CrudRepository<Product, Integer> {
public Product findByName(String name);
}
Besides the default CRUD methods extended from CrudRepository, we declare a custom method findByName() – and by convention, this method will return a Product object by its name. Read this tutorial if you’re new to Spring Data JPA.
5. Configure database connection properties
Next, open the Spring Boot configuration file (application.properties) and specify the properties for database connection as follows:
Make sure that you created the database schema named test in MySQL server. Note that we specify create value for the spring.jpa.hibernate.ddl-auto property, so Hibernate will create the database table upon startup. And it will drop the table if exists.
6. Code Tests for CRUD operations
Create the ProductRepositoryTests class under src/test/java directory with initial code as follows:
package net.codejava;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class ProductRepositoryTests {
@Autowired
private ProductRepository repo;
// test methods for CRUD operations...
}
As you can see, this class will run tests with the actual database.And now, let’s write test methods for testing CRUD operations of ProductRepository.
Test Create operation:
The first one is for testing saving a new product:
I use @Rollback(false) to disable roll back to the data will be committed to the database and available for the next test methods which will run separately. And I use assertThat() method from AssertJ library for more readability than using JUnit’s assertion methods. So you need to add the following imports:
Run this first test method, and you will see Hibernate prints the following output:
Hibernate: drop table if exists products
Hibernate: create table products...
Hibernate: alter table products add constraint...
Hibernate: insert into products (name, price) values (?, ?)
Test Retrieval Operation:
Before writing the second test, set the property spring.jpa.hibernate.ddl-auto to none so Hibernate won’t drop the table in the next run. We want to keep data for the subsequence tests.Code the second test method to test finding a product by name, as follows:
As you can see, first we get the product by its name, then update its price. Then we get the product again, and compare the price. Run this test, you will see Hibernate prints the following SQL statements:
Hibernate: select product0_.id as... from products product0_ where product0_.name=?
Hibernate: update products set name=?, price=? where id=?
Hibernate: select product0_.id as... from products product0_ where product0_.name=?
Test Delete Operation:
The last test method is for testing delete a product. Code this method as follows:
Run this test and you will see Hibernate prints the following SQL statements:
Hibernate: select product0_.id as... from products product0_ where product0_.name=?
Hibernate: delete from products where id=?
Hibernate: select product0_.id as... from products product0_ where product0_.name=?
So that we’ve coded 5 test methods for testing CRUD operations of a Spring Data JPA repository. Now we can run all these tests methods at once. But we need to specify the execution order because JUnit doesn’t run test methods in the order they appear in the code. So we need to use the @TestMethodOrder and @Order annotations as follows:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
@TestMethodOrder(OrderAnnotation.class)
public class ProductRepositoryTests {
@Autowired
private ProductRepository repo;
@Test
@Rollback(false)
@Order(1)
public void testCreateProduct() {
...
}
@Test
@Order(2)
public void testFindProductByName() {
...
}
@Test
@Order(3)
public void testListProducts() {
...
}
@Test
@Rollback(false)
@Order(4)
public void testUpdateProduct() {
...
}
@Test
@Rollback(false)
@Order(5)
public void testDeleteProduct() {
...
}
}
With this update, now we can run the whole test class with in-memory database and/or with create-drop setting.
Conclusion
So far you have learned how to write unit tests for testing CRUD operations for a repository with Spring Data JPA and Hibernate framework. You see Spring and JUnit make it’s easy to write the tests, and AssertJ makes the assertion much more readable. I hope you found this tutorial helpful. You can watch the video version below and download the sample project under the Attachments section.
Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.
Comments