Integration testing using Testcontainers in a Spring Boot 2, JPA application

In this post we will be looking at how to perform integration testing of the repository layer using Testcontainer. We will be setting up Testcontainer in a Spring Boot 2 application, use JPA for persistence and PostgreSQL as database.Besides this we will also be using JUnit5 framework.

Why Integration testing of the database ?

In any software application, it is a known fact that data is of utmost importance. In a web application, this data will usually be handled at the database layer. In an application which uses JPA for persistence, the repository and the JPA entities are major components of the database layer.

To ensure that the repository layer and the entities are functioning appropriately, we should write unit tests. Unit tests for the same are usually written against an in memory database. This is definitely required. The only catch here – Should we wait until production to ensure that the database layer is behaving as expected against our actual type of our database as against the in memory database ?

What would be even better is if we can do integration testing against a similar database that we use in production environment – MySQL,Oracle,PostgreSQL etc. But wouldn’t setting this up be difficult and laborious for just integration testing ?

Enter Testcontainers

Using Testcontainers, we can actually start a containerized instance of the database to test our database layer without going through the pain of actually setting it up on every developer’s machine. Testcontainer is a Java library that provides disposable and lightweight instances of databases. It actually does a lot more that just that. You can read about it here.

How does it help us ?

Using JUnit tests against the actual type of our production database will give us feedback before we actually go into production.This means we can worry less about our database layer and breathe a little easy. There will always be other issues in production,NullPointerExceptions in our code that we can worry about! Since Testcontainers spawns a containerized environment for us to run our tests, we need to have Docker installed.

Getting started with the code

We will be using Spring Boot 2.3.0 along with Spring Data JPA, JUnit 5 to run the tests,Flyway to create tables and TestContainer version 1.14.1.

Snippet of the pom.xml
<!-- The PostgreSQL db driver -->        
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>test</scope>
</dependency> 
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>testcontainers</artifactId>
	<version>${testcontainer.version}</version>
	<scope>test</scope>	
</dependency>
<!-- test container support for postgre module -->
<dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>${testcontainer.version}</version>
        <scope>test</scope>
</dependency>
<!-- JUnit 5 integration with testcontainer -->
<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>junit-jupiter</artifactId>
	<version>${testcontainer.version}</version>
	<scope>test</scope>
</dependency>
The application.properties file
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL94Dialect
spring.jpa.show-sql=true

The spring.jpa.hibernate.ddl-auto has been set to none because we are using flyway to create our database and tables. The initialization script can be found under the /db/migration folder.

Starting the containerized PostgreSQL database and tying it with Spring
public abstract class AbstractContainerBaseTest {

	protected static final PostgreSQLContainer postgres;

	static {
		postgres = new PostgreSQLContainer("postgres:9.6.15")
										 .withDatabaseName("socialmediasite")
										 .withUsername("postgres")
										 .withPassword("password");
		//Mapped port can only be obtained after container is started.
		postgres.start();
	}
	
	static class PropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
		public void initialize(ConfigurableApplicationContext configurableApplicationContext) {			
			TestPropertyValues
					.of("spring.datasource.driver-class-name=" + postgres.getDriverClassName(),
							"spring.datasource.url=" + postgres.getJdbcUrl(),
							"spring.datasource.username=" + postgres.getUsername(),
							"spring.datasource.password=" + postgres.getPassword())
					.applyTo(configurableApplicationContext.getEnvironment());
		}
	}
}

The main purpose of this abstract class is to start the container using Testcontainer library. All other test classes will extend this class .The static block creates the container and this instance will be shared by all test classes. There is a manual call to start method. The library manages to shut it down automatically.

The PropertiesInitializer class is needed because we need to access this containerized database through the Spring Data JPA.

Initialization of Testcontainer,PostgreSQL, Spring Boot,Spring Data JPA
Verifying the output
PostgreSQL container created and closed by Testcontainer

I am using the docker ps –format command to list the containers started by Testcontainer library. As shown in the image above, one can see the container having the name competent_raman which is the PostgreSQL container.For all the test cases this is the single instance of the container. There is also this additional image quay.io/testcontainers/ryuk:0.2.3 – this is created by the Testcontainer library which ensures all other containers are shut down and cleaned when the JVM shuts down.

Database and tables in the containerized PostgreSQL database.

As you can see in the image above, I am using the docker exec -it command to connect to the same containerized instance, (competent_raman) of the PostgreSQL database. Then using the command psql -h 192.168.99.100 -p 32813, we are connected to the dockerized database. The -p indicates the port number and -h the host. The \c socialmediasite command connects us to the database and finally the \d command is used to list all the tables.

Once all the tests are executed, the Testcontainer library removes the instances of the containers that it created.

The JPA entities SocialMediaSite and User used in the application have a One-Many relationship.

Conclusion

Writing unit tests against an in-memory database like H2 is certainly helpful but writing integration tests against actual type of production database takes us to the next level – more confidence, fewer failures in production. The combination of Testcontainers, Spring Boot 2, JUnit5 makes it easy for us to get started on this journey. You can find the entire source code here.

Understanding @SpringBootApplication

The objective of this post is to get an understanding of what @SpringBootApplication annotation does in a Spring Boot application. Sometimes we just tend to add an annotation, everything works magically and we are happy.

But when we want to make a few changes, things start breaking, components are not found and then we blame the magic that we enjoyed earlier.So it is always a good idea to get a better understanding of what some of these annotations do.

In a Spring Boot application, we need a class with a main method annotated with @SpringBootApplication annotation as shown below:

package com.boot.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaApplication.class, args);
	}

}

This is the starting point for a Spring Boot application. The @SpringBootApplication annotation is really a short hand annotation or a combination of the following annotations:

1. @SpringBootConfiguration

This signifies that the application is not just a normal Spring application but a Spring Boot application. Actually this is just a combination of @Configuration+ Spring Boot, so more of a specialization. @Configuration applied on a file signifies that the class contains spring bean configurations using @Bean.

2. @EnableAutoConfiguration

This is the super intelligent annotation which kicks in the auto configuration for Spring Boot. Auto configuration is done by inspecting the classpath and configuring necessary beans that may be required. If you were to provide your own configuration, then Spring Boot will not re-configure the bean again.

How is this auto configuration done ?

Auto Configuration is done via a SpringFactoriesLoader class which will pick up and read the META-INF/spring.factories file.  It is like a hook into instantiating certain beans found on the classpath. A sample of the same is shown below. EnableAutoConfiguration class is the ‘key’ below and it can have many ‘values’ as classes. These are read and conditionally configured depending on what is already configured by the developer and what is found on the classpath. For example, only if RabbitMQ and Spring AMQP client are found on the classpath usually added via maven or gradle, then Spring Boot tries to configure RabbitMQ.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
....

3. @ComponentScan

Any Spring application will usually consist of beans which will be managed by Spring framework. To tell Spring framework to manage a bean, we use @ComponentScan. This annotation will scan all your beans and register it with the Spring Application Context. Later, you could inject the beans using @Autowired. Examples of a Component are classes annotated with @Controller, @Repository, @Service etc.

One has to be careful as @ComponentScan will scan the packages below the package from where this is defined.

A better way to organize your packages would be to have the  class containing the @SpringBootApplication in the root package of your application so that all sub packages get scanned and beans are registered with application context. If this is not possible for some reason, you can of course add @ComponentScan annotation and specify the base package(s) to scan for spring beans.

Conclusion

Annotations in the Spring or Spring Boot framework makes the life of a developer easy and increases productivity.However it is important to understand the ‘what and how’ of these annotations to be a more effective and efficient developer.

Testing JPA entities using Spring Boot 2, JUnit 5 and Java 14

In this article we will be looking at how to get started with testing JPA entities and the repository layer using Spring Boot 2.2 , JUnit 5 and Java 14. I have written a similar post here which uses Spring 1.5, JUnit 4 and Java 8. As the changes are significant I decided to keep them separate instead of updating that post.

I will be focusing mostly about the changes I had to make to the code to upgrade the libraries mentioned above. You can find the complete source code on github here.

Let us consider the same example of One-Many relation between SocialMediaSite and a User.

The @DataJpaTest

This remains the key ingredient behind running the JPA related tests in Spring Boot. @DataJpaTest disables full auto configuration and applies configuration related to JPA tests only. This concept hasn’t changed between Spring Boot 1.5 and 2.x. Now, let’s take a look at the areas where the code needed modifications.

Changes to the pom.xml

Upgrading the spring boot version to 2.2.6

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
</parent>

Upgrading java version to 14

<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>14</java.version>
	</properties>

Upgrading the dependency section to exclude JUnit 4

The spring-boot-starter-test dependency includes the vintage JUnit 4 and JUnit 5 dependencies.Since we will be using JUnit 5, we will exclude the vintage one.

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

Junit 5 consists of 3 different sub projects – The JUnit engine , JUnit vintage ( Junit 3 and 4) and JUnit Jupiter( JUnit5). All JUnit 5 annotations reside in org.junit.jupiter.api package.

Support for preview features in Java 14

<plugin>
	<artifactId>maven-compiler-plugin</artifactId>
	<configuration>
		<release>14</release>
		<compilerArgs>
		    <arg>--enable-preview</arg>
		 </compilerArgs>
					 
                <forceJavacCompilerUse>true</forceJavacCompilerUse>
		  <parameters>true</parameters>
	</configuration>
</plugin>

<plugin>
	<artifactId>maven-surefire-plugin</artifactId>
	<configuration>
		<argLine>--enable-preview</argLine>
	</configuration>
</plugin>
Use of the Record type

Record type has been added as a preview feature in Java 14. The EmailAddress class has now been modified as a Record type. This was a value object before.

public record EmailAddress(String emailAddress) {

	public static final Pattern VALID_EMAIL_ADDRESS_REGEX = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$",
			Pattern.CASE_INSENSITIVE);

	public EmailAddress {
		Assert.isTrue(isValid(emailAddress), "Email Address is Invalid !");
	}

	private static boolean isValid(String emailAddress) {
		return emailAddress != null ? VALID_EMAIL_ADDRESS_REGEX.matcher(emailAddress).matches() : false;
	}

}
Changes to the Test classes
  1. There is no need to add the @RunWith(SpringRunner.class) annotation anymore.
  2. The @Before annotation has been now replaced with @BeforeEach to perform any kind of setup before each test. Similarly the @After has been replaced with @AfterEach.
  3. The @Rule and @ExpectedException had been removed from JUnit5. The test classes have been refactored to reflect the same. The assertThrows and assertEquals method from the JUnit5 library have been used instead.
@Test
public void testShouldReturnExceptionForInvalidEmailAddress() {
		
	var exception = assertThrows(IllegalArgumentException.class, () -> new EmailAddress("test@.com"));
	assertEquals(exception.getMessage(), "Email Address is Invalid !");
}

The assertThrows method takes a functional interface, Executable as second parameter. We pass the block of code that needs to be executed to Executable.

Conclusion

The @DataJpaTest helps us to test the JPA entities and the repository layer. This is also called as test slicing as we are testing only the JPA components.The @DataJpaTest annotation contains @ExtendWith(SpringExtension.class) which integrates the Spring Testing framework with JUnit 5 model.

We have not explored many features of JUnit5 in this example but the combination of Spring Boot’s testing features and JUnit5 does make a powerful combination.

Using Spock to test JPA entities in a Spring Boot application

In my previous post we saw how to test JPA entities along with the Spring Data repository layer in a Spring Boot based application. We made use of JUnit and the AssertJ library.

In this post we will look at how to use the Spock framework to test the same. In fact, in this example, I will be using a combination of Spock based tests along with JUnit tests.To know more about the Spock framework, view the official site here. Spock is a testing and specification framework for Java and Groovy applications.

Let’s get started…

Setup using maven: ( pom.xml)  

<properties>
<spock.version>1.1-groovy-2.4</spock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
<source>1.8</source>
<target>1.8</target>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.9.2-01</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>2.4.3-01</version>
</dependency>
</dependencies>
</plugin>
</plugins>

view raw
spock_spring_pom.xml
hosted with ❤ by GitHub

  1. We need to add the spock-spring dependency. This will bring in the dependencies required to run Spock based tests in a Spring boot based application.
  2. Notice the use of <spock.version>1.1-groovy-2.4</spock.version>. We are overriding the spock version. Spring Boot 1.5.4 brings in version 1.0 of Spock, however this needs a @ContextConfiguration to run Spock based tests in a Spring Boot application. Overriding the version to 1.1 removes the need to add this annotation.
  3. In the plugin section , we need to add the dependency for groovy-eclipse-compiler which will compile the groovy code. Spock is based on groovy and hence using Spock to write tests means we write groovy code.

Test classes using Spock

As mentioned in my earlier post, let us consider the same example of 2 JPA entities, SocialMediaSite and Users ( OneToMany). A User has an email which we represented as EmailAddress value object.  The test class for this looks as follows:

EmailAddressTest.groovy

package com.boot.test.db;
import com.boot.entity.EmailAddress;
import spock.lang.Specification
public class EmailAddressTest extends Specification {
private static final String testEmail = "test@test.com";
def "throw exception for invalid format of email address"(String emailAddress)
{
when:
"create invalid email address"
def email = new EmailAddress(emailAddress);
then:
"throw exception for invalid format of email address"
def e = thrown(IllegalArgumentException)
e.message == "Email Address is Invalid !"
where:
emailAddress | _
"test@.com" | _
" " | _
null | _
}
def "return valid email address"()
{
when:
"create new email address"
def email = new EmailAddress(testEmail);
then:
"return valid email"
email.toString() == testEmail;
}
}

  1. The test class extends spock.lang.Specification. This is how you begin writing a Spock based test.
  2. Notice the method names are strings, nice descriptive methods names.
  3. The when/then syntax is for assertions. It’s like saying, “Hey, when this happens then check these things”.
  4. The where section is the first test method above is data driven.Notice the first 2 columns, emailAddress and a blank. This is because data driven tables in Spock need 2 columns. We need just one. The next rows supply data to the same method.Hence this method is run with all the values mentioned in the first column starting from the 2nd row. Now that is awesome compared to writing multiple methods which do the same thing or if you are using TestNG, this is done using a DataProvider.
  5. Notice that we have not used any Assertion library here. In Spock this is done using ==.

SocialMediaSiteRepositoryTest.groovy

package com.boot.test.db;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import com.boot.entity.EmailAddress;
import com.boot.entity.SocialMediaSite;
import com.boot.entity.User;
import com.boot.repository.SocialMediaSiteRepository;
import spock.lang.Specification
@DataJpaTest
public class SocialMediaSiteRepositoryTest extends Specification{
@Autowired
private SocialMediaSiteRepository socialMediaSiteRepository;
def facebook = new SocialMediaSite("Facebook", "Online Social Media and Networking Service")
def "find social media site FaceBook by Id" () {
def savedFaceBookEntity = socialMediaSiteRepository.save(facebook)
when: "load facebook entity"
def faceBookEntityFromDb = socialMediaSiteRepository.findOne(savedFaceBookEntity.getId())
then:"saved and retrieved entity by id must be equal"
savedFaceBookEntity.getId() == faceBookEntityFromDb.getId()
}
def "find social media site facebook by name"() {
def savedFaceBookEntity = socialMediaSiteRepository.save(facebook)
when: "find by name FaceBook"
def socialMediaEntity = socialMediaSiteRepository.findByName("Facebook")
then: "saved and retrieved entity by name must be equal"
socialMediaEntity == savedFaceBookEntity
}
def "add users to social mediate site users " () {
setup:
def firstUser = new User("Mar", "Zuckerber", new EmailAddress("mark@mark.com"));
facebook.addUser(firstUser);
def savedFaceBookEntityWithUsers = socialMediaSiteRepository.save(facebook)
when:
def faceBookEntityFromDB = socialMediaSiteRepository.findOne(savedFaceBookEntityWithUsers.getId())
then:
faceBookEntityFromDB.getUsers() != null
faceBookEntityFromDB.getUsers().size() == 1;
}
}

Notice the @DataJpaTest annotation on the class. 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. There is no need to add @ContextConfiguration as we are using Spock 1.1.

Running Spock tests and JUnit tests together

I have added the 2 groovy test files in src/test/groovy.  We can have tests written in JUnit too. I have a JUnit based test class in src/test/java. The groovy eclipse compiler dependency we added in the pom.xml compiles and run tests from both the packages.

Conclusion

This is my first experience with Spock framework and I have thoroughly enjoyed writing tests with it ! There is of course a lot more to the Spock framework. I hope you have enjoyed this quick introduction to Spock for testing Spring Data Repository and JPA entities in a Spring Boot application. The synergy between Spock and the testing changes made in Spring Boot since version 1.4 (test slices) is great !

You can find the project on github.

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.
}

view raw
SocialMediaSite.java
hosted with ❤ by GitHub

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 .

Spring Boot and Hibernate – Quick Introduction

It has been a while since I wrote this blog about how I  discovered Spring Boot. A surgery to fix my ACL and meniscus tear has kept me away from a lot of things, more about that in a different blog. But it is time we got a little intimate with Spring Boot.

In this blog we will look at how to setup a simple Spring Boot application with JPA/Hibernate. I remember shying away from writing/experimenting any code whenever I heard – setting up something with Spring. Those were the dark ages ! Well, if you haven’t tried the super cool Spring Initializr yet , it is high time you did. You can setup Spring based projects in an absolute flash !

All I did was enter JPA, H2 in the Search for dependencies  box and imported the project in my IDE.

spring-boot-jpa-intializr

Dependencies

The pom.xml from the imported project contains the following important dependencies:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

We added 2 things in the search box, JPA and H2 and so now the pom.xml contains dependencies corresponding to that:

  1. The spring-boot-started-data-jpa dependency brings in the latest version of the Hibernate, support for transactions, spring-data-jpa etc. Not only does it bring in the latest versions but also compatible versions. Notice that we need not add spring-orm and all the pain to setup the latest and compatible versions of Hibernate, JPA, transactions. This is done automatically for us.
  2. The h2 dependency above is for an embedded database that we will be using. One can easily replace that with the respective database in your project. Since h2 database will be present on the classpath, Spring Boot will configure the h2 database for us. If you add the dependency for Mysql database, Spring Boot will help configuring a MySql database.

JPA Entity

To keep things simple, we are going to create a Project entity which contains id, name and description and persist this  data. We will use Spring Data to persist and fetch the data.

Application starting point

The starting point in our application is the SpringBootJpaApplication.java:

package com.boot.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}

The output from running the file above: (lines 4 – 8)

2017-06-06 00:56:10.523 INFO 616 — [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-06-06 00:56:10.812 INFO 616 — [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2017-06-06 00:56:11.204 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 1 , name = 'Java' , description = 'Java based project for a bank']
2017-06-06 00:56:11.205 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 2 , name = 'Scala' , description = 'Scala based project for an oil company']
2017-06-06 00:56:11.206 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Project[ id = 3 , name = 'Apache Kafka' , description = 'Kafka based project for a data mining company']
2017-06-06 00:56:11.206 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Getting project by name
2017-06-06 00:56:11.268 INFO 616 — [ main] com.boot.jpa.ProjectInitRunner : Scala based project id 2

Sample Data added via the Initializer

In addition to the SpringBootJpaApplication.java, there is a ProjectInitRunner.java which is run by Spring Boot on startup. It contains initialization code which inserts some Project entities into the H2 database and then fetches one of the project details. This class is run on start up as it implements the CommandLineRunner interface. I am currently using it to persist some data into the H2 database.

package com.boot.jpa;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ProjectInitRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(ProjectInitRunner.class);
@Autowired
private ProjectRepository projectRepository;
@Override
public void run(Stringargs) throws Exception {
// Save
projectRepository.save(new Project("Java", "Java based project for a bank"));
projectRepository.save(new Project("Scala", "Scala based project for an oil company"));
projectRepository.save(new Project("Apache Kafka", "Kafka based project for a data mining company"));
// Find all projects.
List<Project> projects = projectRepository.findAll();
projects.forEach(project > logger.info(project.toString()));
// Get a project by name
logger.info("Getting project by name");
Project project = projectRepository.findByName("Scala");
logger.info("Scala based project id " + project.getId());
}
}

The Spring Boot starter data jpa brings in the Spring Data project which we are using to persist and retrieve the data.

Package structure

This is how the package structure looks :

spring-boot-sts-package

 

Gotchas – Packages and Spring  component scanning

Since all the classes were in the same package, the @SpringBootApplication annotation simply scanned and picked up all classes which were in the same package. This works as the @SpringBootApplication does Auto Configuration for us via the @EnableAutoConfiguration annotation. This would also work if other Spring beans were in sub or child packages.

If the Repository and the entities were in a different package with respect to the SpringBootJpaApplication.java which contains the main method, we need to do slightly more work:

spring-boot-sts-split-package

As shown above, Project.java and ProjectRepository.java are in a completely different package as compared to the class containing the main method. We need to now inform Spring Boot to enable/scan the repository and entity related classes. This is simply done by modifying the SpringBootJpaApplication.java and adding 2 annotations, @EnableJpaRepositories and @EntityScan. The annotations contain the information about packages that need to be scanned to enable the Jpa repository and scan the JPA entity related classes.No other change is needed and everything runs as usual.

package com.boot.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages="com.boot.repository")
@EntityScan(basePackages = "com.boot.repository")
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}

Conclusion

So it is really quite simple and super quick to setup a Spring Boot JPA project. The starter projects brings in all the required dependencies and also provides us with default and most importantly right and latest configurations if we don’t specify any. We can always override the default configurations by adding our own and Spring Boot will happily skip that part.

I think in case of Spring Boot, we can really say – Less (no?) Pain, More Gain !

All the code can be found here : Github