In this tutorial, we will learn how to do Spring Boot application Integration Testing using Testcontainers.
Check out my Spring boot testing Udemy course: Testing Spring Boot Application with JUnit and Mockito (Includes Testcontainers)
First, we write Integration tests using a local MySQL database, and then we will address the problem with Testcontainers as a solution.
SpringBoot provides excellent support for integration testing using @SpringBootTest annotation. We can use @SpringBootTest annotation to load the application context and test various components.
@SpringBootTest will bootstrap the full application context, which means we can @Autowire any bean that's picked up by component scanning into our test.
In this tutorial, we are going to use @SpringBootTest annotation for Integration testing.
YouTube Video
Tools and technologies used
- Java 11+
- Spring Boot
- Spring Data JPA
- MySQL
- Lombok
- JUnit 5 Framework
- IntelliJ IDEA
- Testcontainers
- Docker
- Maven
What is Integration Testing
As the name suggests, integration tests focus on integrating different layers of the application. That also means no mocking is involved.
Basically, we write integration tests for testing a feature that may involve interaction with multiple components.
Examples: Integration testing of complete Employee Management Feature ( EmployeeRepository, EmployeeService, EmployeeController).
Integration testing of complete User Management Feature (UserController, UserService, and UserRepository).
Integration testing of complete Login Feature (LoginRespository, LoginController, Login Service), etc
Development Steps
- Create Spring Boot Application
- Configure MySQL database
- Create JPA Entity
- Create Spring Data JPA Repository
- Create Spring Boot REST Controller
- Create Integration Tests with MySQL database
- What Testcontainers
- Adding Testcontainers to Spring Boot Project
- Write Integration Tests using Testcontainers
- Demo
1. Create Spring Boot Application
- Spring Web
- Spring Data JPA
- Lombok
- MySQL Driver
2. Configure MySQL database
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = create-drop
3. Create JPA Entity
package net.javaguides.spirngboot.entity;
import lombok.*;
import javax.persistence.*;
@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "students")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String email;
}
4. Create Spring Data JPA Repository
package net.javaguides.spirngboot.repository;
import net.javaguides.spirngboot.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository extends JpaRepository<Student, Long> {
}
5. Create Spring Boot REST Controller
package net.javaguides.spirngboot.controller;
import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/students")
public class StudentController {
@Autowired
private StudentRepository studentRepository;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Student createStudent(@RequestBody Student student){
return studentRepository.save(student);
}
@GetMapping
public List<Student> getAllStudents(){
return studentRepository.findAll();
}
}
6. Create Integration Tests with MySQL database
package net.javaguides.spirngboot;
import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.List;
@SpringBootTest
@AutoConfigureMockMvc
class SpringbootTestcontainersDemoApplicationTests {
@Autowired
private StudentRepository studentRepository;
@Autowired
private MockMvc mockMvc;
// given/when/then format - BDD style
@Test
public void givenStudents_whenGetAllStudents_thenListOfStudents() throws Exception {
// given - setup or precondition
List<Student> students =
List.of(Student.builder().firstName("Ramesh").lastName("faadatare").email("ramesh@gmail.com").build(),
Student.builder().firstName("tony").lastName("stark").email("tony@gmail.com").build());
studentRepository.saveAll(students);
// when - action
ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));
// then - verify the output
response.andExpect(MockMvcResultMatchers.status().isOk());
response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));
}
}
@Autowired
private MockMvc mockMvc;
ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));
// then - verify the output
response.andExpect(MockMvcResultMatchers.status().isOk());
response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));
7. Run Integration Test
What is the problem with the Integration test that we have written?
8. What is Testcontainers?
9. Adding Testcontainers to Spring Boot Project
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.16.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.16.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.16.2</version>
<scope>test</scope>
</dependency>
Write Integration Tests using Testcontainers
Let's change the Integration test to use Testcontainers.Let us create a base class AbstractContainerBaseTest so that all our integration tests can extend without repeating the common configuration.
package net.javaguides.spirngboot;
import org.testcontainers.containers.MySQLContainer;
public class AbstractContainerBaseTest {
static final MySQLContainer MY_SQL_CONTAINER;
static {
MY_SQL_CONTAINER = new MySQLContainer("mysql:latest");
MY_SQL_CONTAINER.start();
}
}
Now, simply extend our Integration class with the above AbstractContainerBaseTest:
package net.javaguides.spirngboot;
import net.javaguides.spirngboot.entity.Student;
import net.javaguides.spirngboot.repository.StudentRepository;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.List;
@SpringBootTest
@AutoConfigureMockMvc
class SpringbootTestcontainersDemoApplicationTests extends AbstractContainerBaseTest{
@Autowired
private StudentRepository studentRepository;
@Autowired
private MockMvc mockMvc;
// given/when/then format - BDD style
@Test
public void givenStudents_whenGetAllStudents_thenListOfStudents() throws Exception {
// given - setup or precondition
List<Student> students =
List.of(Student.builder().firstName("Ramesh").lastName("faadatare").email("ramesh@gmail.com").build(),
Student.builder().firstName("tony").lastName("stark").email("tony@gmail.com").build());
studentRepository.saveAll(students);
// when - action
ResultActions response = mockMvc.perform(MockMvcRequestBuilders.get("/api/students"));
// then - verify the output
response.andExpect(MockMvcResultMatchers.status().isOk());
response.andExpect(MockMvcResultMatchers.jsonPath("$.size()", CoreMatchers.is(students.size())));
}
}
10. Demo
Before running the Integration test make sure that Docker is running on your machine otherwise, you won't be able to run the Integration test.
Here is the output of the above Integration test using Testcontainers:
GitHub Repository
The complete source code of this tutorial is on my GitHub repository at https://github.com/RameshMF/springboot-testcontainers-demo
Conclusion
In this tutorial, we have discussed how to perform Spring Boot application Integration Testing using Testcontainers.
If you want to learn more about Spring boot testing then highly suggest my Udemy course:
Testing Spring Boot Application with JUnit and Mockito (Includes Testcontainers)
Comments
Post a Comment
Leave Comment