Testing JPA entities in a Spring Boot application

In this blog we will look at how to get started with testing JPA entities and Spring Data Repository in a Spring Boot based application. We will be using JUnit for the same.

I have observed that a good number of projects do not write any tests for JPA entities or the repository layers which make use of the entities to perform CRUD operations. Writing tests for JPA entities and Spring data repositories can be really effective in checking if all the entities are mapped correctly and ensuring that the repository methods implemented by Spring Data along with the custom methods that you write are behaving in the right way. After all , most applications always talk to a database and if your data is not being handled properly, what is the point of having a great user interface or a well designed business layer ?

Since Spring Boot 1.4, testing these layers has become quite easy and more focused. Let us consider a simple One-Many relation between 2 entities, SocialMediaSite and  a User.

A SocialMediaSite can have many users which is mapped using the @OneToMany JPA annotation.

SocialMediaSite.java 


@Entity
public class SocialMediaSite {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
@OneToMany(mappedBy = "socialMediaSite", cascade = CascadeType.ALL, orphanRemoval = true)
private List<User> users = new ArrayList<>();
//Other details.
}

User.java


@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
@Convert(converter=EmailAddressConverter.class)
private EmailAddress email;
@ManyToOne
private SocialMediaSite socialMediaSite;
//Other details.
}

view raw

User.java

hosted with ❤ by GitHub

Notice that EmailAddress is  a value object.

SocialMediaRepository.java – This is a Spring Data Repository interface( a proxy instance is created via Spring to back this interface)


public interface SocialMediaSiteRepository extends JpaRepository<SocialMediaSite, Long> {
SocialMediaSite findByName(String name);
}

SocialMediaSiteEntityTest.java –  Test the JPA entities.


package com.boot.test.db;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import com.boot.entity.EmailAddress;
import com.boot.entity.SocialMediaSite;
import com.boot.entity.User;
@RunWith(SpringRunner.class)
@DataJpaTest
public class SocialMediaSiteEntityTest {
@Autowired
private TestEntityManager entityManager;
@Rule
public ExpectedException thrown = ExpectedException.none();
private SocialMediaSite facebook;
private User firstUser;
private User secondUser;
@Before
public void setUp() {
facebook = new SocialMediaSite("Facebook", "Online Social Media and Networking Service");
firstUser = new User("Mar", "Zuckerber", new EmailAddress("mark@mark.com"));
secondUser = new User("Chri", "Hughe", new EmailAddress("chris@chris.com"));
}
@Test
public void saveSocialMediaSiteFacebook() {
SocialMediaSite savedFacebookData = this.entityManager.persistAndFlush(facebook);
assertThat(savedFacebookData.getName()).isEqualTo("Facebook");
}
@Test
public void createSocialMediaSiteFacebookNullNameException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Social Media Site Name should not be empty");
new SocialMediaSite("", "Online Social Media and Networking Service");
}
@Test
public void saveFacebookUsers() {
facebook.addUser(firstUser);
facebook.addUser(secondUser);
SocialMediaSite savedFaceBook = this.entityManager.persistFlushFind(facebook);
assertThat(savedFaceBook.getId()).isNotNull();
assertThat(savedFaceBook.getName()).isEqualTo("Facebook");
assertThat(savedFaceBook.getUsers().size()).isEqualTo(2);
}
@Test
public void removeFacebookUserChri() {
facebook.addUser(firstUser);
facebook.addUser(secondUser);
SocialMediaSite savedFaceBook = this.entityManager.persistFlushFind(facebook);
savedFaceBook.removeUser(secondUser);
SocialMediaSite updatedData = this.entityManager.persistAndFlush(savedFaceBook);
assertThat(updatedData.getUsers().size()).isEqualTo(1);
assertThat(updatedData.getUsers().get(0).getFirstName()).isEqualTo("Mar");
}
}

The setUp method annotated with the @Before annotation above initializes some mock data that we can use for the tests.

The key takeaways from this class:

  1.  @RunWith(SpringRunner .class) – This brings together JUnit and the Spring test  module. The  SpringRunner class extends SpringJUnit4ClassRunner, so it is pretty  much the  same that was used earlier. Shorter class names are always pleasing to  the eye.
  2.  @DataJpaTest   This is the most important annotation for testing JPA entities in a  Spring Boot application. It  spawns an in-memory data base and runs our tests  against it. Along with this, the  JPA entities are scanned, Transactions, Hibernate  and Spring Data are also configured.
  3.  TestEntityManager   The @DataJpaTest also configures a TestEntityManager, this  is an alternative to the EntityManager. It actually makes use of the EntityManager  but has a nice set of methods like persistAndGetId, persistAndFlush etc.
  4.  AssertJ  The code above uses the AssertJ library to perform all the assertions, this  is a nice way to get all the assertions done very fluently ! This is pulled in by the  spring-boot-starter-test dependency.
  5.  Junit – This is also pulled in by the spring-boot-starter-test dependency

On similar lines, the tests for the Repository class can also be written as shown below.

SocialMediaSiteRepositoryTest .java


package com.boot.test.db;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.boot.entity.EmailAddress;
import com.boot.entity.SocialMediaSite;
import com.boot.entity.User;
import com.boot.repository.SocialMediaSiteRepository;
@RunWith(SpringRunner.class)
@DataJpaTest
public class SocialMediaSiteRepositoryTest {
@Autowired
private SocialMediaSiteRepository socialMediaSiteRepository;
private SocialMediaSite facebook;
@Before
public void setUp() {
facebook = new SocialMediaSite("Facebook", "Online Social Media and Networking Service");
}
@Test
public void saveFacebookAndFindById() {
facebook = socialMediaSiteRepository.save(facebook);
assertThat(socialMediaSiteRepository.findOne(facebook.getId())).isEqualTo((facebook));
}
@Test
public void saveFacebookAndFindBySocialMediaSiteName() {
facebook = socialMediaSiteRepository.save(facebook);
assertThat(socialMediaSiteRepository.findByName("Facebook")).isEqualTo((facebook));
}
@Test
public void saveFacebookUsers() {
User firstUser = new User("Mar", "Zuckerber", new EmailAddress("mark@mark.com"));
facebook.addUser(firstUser);
facebook = socialMediaSiteRepository.save(facebook);
// user added is not null
assertThat(socialMediaSiteRepository.findOne(facebook.getId()).getUsers()).isNotNull();
// check the one user we added.
assertThat(socialMediaSiteRepository.findOne(facebook.getId()).getUsers().size()).isEqualTo(1);
}
}

Conclusion

As you can see, testing JPA entities and the repository layer in a Spring Boot based application is quite simple. We don’t need configuration for the entire application(all layers) to test the database related functionality. Using the @DataJpaTest in Spring boot helps us in configuring and testing just the JPA section in the code.

Writing good tests for the JPA/Hibernate entities and the repository layer against an embedded database can be extremely useful in the long run. Any changes in the database schema or in the entity mapping which might lead to issues at run time can be caught immediately. In addition one can also see the SQL queries being executed which can be extremely useful.

You can find the code on github.

Note: In case you are are interested in testing JPA entities using Spring Boot 2, JUnit 5 and Java 14 , read my post here .

One thought on “Testing JPA entities in a Spring Boot application”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: