Avoid running different databases between integration tests and production.
In general, we tend to use H2 to perform integration tests within the application. However there are scenarios where H2 may not give the same outcome as our actual database, such as MySQL. Such scenario is when you have a table column called rank or order.
Both names are allowed with H2 database but not with MySQL as those are reserved keywords. Therefore it is best to use the same database, in production environment, for our integration tests.
In this guide, we will implement MySQL Container, from TestContainers, with Spring Boot.
Full dependencies can be found in pom.xml.
spring-boot-starter-data-jpa
spring-boot-starter-data-rest
mysql-connector-java
junit-jupiter
from TestContainersmysql
from TestContainers
Given we have a class called Book along with its repository class, BookRepository.
@Data
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
@Embedded
private Author author;
private String title;
}
public interface BookRepository extends JpaRepository<Book, Long> {
}
Here we will be utilizing MySQL module from TestContainers to perform integration tests. The following implementation can be found in BookRepositoryRestResourceTests
org.testcontainers:junit-jupiter
dependency simplifies our implementation whereby the dependency will handle the
start and stop of the container.
We will start by informing @SpringBootTest
that we will be using ContainerDatabaseDriver
as our driver class
along with our JDBC URL
@Testcontainers
@SpringBootTest(
properties = {
"spring.jpa.generate-ddl=true",
"spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver",
"spring.datasource.url=jdbc:tc:mysql:8:///test
}
)
public class BookRepositoryRestResourceTests {
}
We will trigger a REST call to create a Book and given that there is a database running, the book should be created.
@Testcontainers
@SpringBootTest(
properties = {
"spring.jpa.generate-ddl=true",
"spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver",
"spring.datasource.url=jdbc:tc:mysql:8:///test
},
webEnvironment = RANDOM_PORT
)
public class BookRepositoryRestResourceTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
@DisplayName("Entity will be created if datasource is available")
void create() {
var author = author();
var book = book(author);
ResponseEntity<Book> response = restTemplate.postForEntity("/books", book, Book.class);
assertThat(response.getStatusCode()).isEqualTo(CREATED);
}
private Author author() {
var author = new Author();
author.setName("Rudyard Kipling");
return author;
}
private Book book(final Author author) {
var book = new Book();
book.setAuthor(author);
book.setTitle("The Jungle Book");
return book;
}
}
Execute the test and you will get HTTP 200
or CREATED
returned. To be certain that our test did run with
MySQL Container, we should see the following content in the logs:
DEBUG 🐳 [mysql:8] - Starting container: mysql:8
...
org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
This is how the application informing us that it is using MySQL Container which lead to Spring Boot automatically
configure our dialect to MySQL8Dialect
.
Another option is to verify that our application will connect to MySQL by triggering a check against Spring Boot Actuator Health endpoint.
public class DatasourceHealthTests {
@Test
@DisplayName("Database status will be UP and Database name should be MySQL")
void databaseIsAvailable() throws JsonProcessingException {
var response = restTemplate.getForEntity("/actuator/health", String.class);
assertThat(response.getBody()).isNotNull();
JsonNode root = new ObjectMapper().readTree(response.getBody());
JsonNode dbComponentNode = root.get("components").get("db");
String dbStatus = dbComponentNode.get("status").asText();
String dbName = dbComponentNode.get("details").get("database").asText();
assertThat(dbStatus).isEqualTo("UP");
assertThat(dbName).isEqualTo("MySQL");
}
}
Test above verifies that there's a running MySQL database connected to the application. Full implementation can be found in DatasourceHealthTests.
Now that we are running the same database as production environment, we can expect more accurate results from our integration tests.