Vous pouvez utiliser Testcontainers (https://www.testcontainers.org/) pour démarrer un conteneur MySQL uniquement lors du test de JUnit.
Exemple de code SimpleMySQLTest.java Il semble être assez facile à utiliser, mais j'ai eu beaucoup de mal quand je l'ai essayé avec Spring Boot + MyBatis, donc je vais résumer son fonctionnement.
Avant d'essayer le test avec Testcontainers, écrivez du code fonctionnel.
Créez trois fichiers: UserRepositoryImpl.java, UserMapper.java et UserMapper.xml.
UserRepositoryImpl.java
package springdockerexample.infrastructure.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import springdockerexample.domain.user.Name;
import springdockerexample.domain.user.User;
import springdockerexample.domain.user.UserRepository;
import springdockerexample.domain.user.Users;
import java.util.List;
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private UserMapper mapper;
@Override
public Users findAll() {
List<User> users = mapper.selectAll();
return new Users(users);
}
}
UserMapper.java
package springdockerexample.infrastructure.user;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import springdockerexample.domain.user.Name;
import springdockerexample.domain.user.User;
import java.util.List;
@Mapper
public interface UserMapper {
List<User> selectAll();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="springdockerexample.infrastructure.user.UserMapper">
<resultMap id="user" type="springdockerexample.domain.user.User">
<result property="name.value" column="name"/>
<result property="age.value" column="age"/>
</resultMap>
<select id="selectAll" resultMap="user">
SELECT name, age FROM users
</select>
</mapper>
Écrivez les informations de connexion à la base de données dans application.yaml.
application.yaml
spring:
datasource:
url: jdbc:mysql://localhost/mydb
username: user
password: password
driverClassName: com.mysql.cj.jdbc.Driver
Préparez les données pour le développement et les tests dans un fichier.
sql:src/test/resources/docker-entrypoint-initdb.d/init.sql
USE `mydb`;
CREATE TABLE `users` (
`name` VARCHAR(255) NOT NULL,
`age` int NOT NULL
);
INSERT INTO `users` (`name`, `age`) VALUES
('Alice', 20),
('Bob', 30);
Pour vérifier le fonctionnement de Spring Boot + MyBatis, démarrez MySQL avec Docker Compose.
A ce stade, en montant le fichier SQL préparé précédemment sur docker-entrypoint-initdb.d, les données seront insérées automatiquement.
docker-compose.yaml
version: '3'
services:
my-db:
image: mysql:5.7.25
ports:
- 3306:3306
volumes:
- ./src/test/resources/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mydb
MYSQL_USER: user
MYSQL_PASSWORD: password
Si vous incluez spring-dev-tools, il commencera par ./mvnw spring-boot: run
.
$ ./mvnw spring-boot:run
$ curl localhost:8080/users
{"users":[{"name":"Alice","age":20},{"name":"Bob","age":30}]}
Cela fonctionne en toute sécurité.
De cette façon, il est possible de tester avec un conteneur démarré avec Docker Compose, mais il n'est pas possible de redémarrer le conteneur pour chaque cas de test.
Utilisez Testcontainers pour lancer des conteneurs à volonté pendant les tests JUnit.
Pour les tests, reportez-vous à "Préparer un conteneur de base de données jetable avec des conteneurs de test et tester l'application Spring Boot" Créez les fichiers suivants pour démarrer le conteneur MySQL et définissez les informations de connexion sur le contexte.
MySQLContainerContextInitializer.java
package springdockerexample.testhelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
public class MySQLContainerContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final String MYSQL_IMAGE = "mysql:5.7.25";
private static final String DATABASE_NAME = "mydb";
private static final String USERNAME = "user";
private static final String PASSWORD = "password";
private static final int PORT = 3306;
private static final String INIT_SQL = "docker-entrypoint-initdb.d/init.sql";
private static final String INIT_SQL_IN_CONTAINER = "/docker-entrypoint-initdb.d/init.sql";
private static final Logger LOGGER = LoggerFactory.getLogger(MySQLContainerContextInitializer.class);
private static final MySQLContainer MYSQL = (MySQLContainer) new MySQLContainer(MYSQL_IMAGE)
.withDatabaseName(DATABASE_NAME)
.withUsername(USERNAME)
.withPassword(PASSWORD)
.withExposedPorts(PORT)
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.withClasspathResourceMapping(INIT_SQL, INIT_SQL_IN_CONTAINER, BindMode.READ_ONLY);
static {
MYSQL.start();
}
@Override
public void initialize(ConfigurableApplicationContext context) {
String mysqlJdbcUrl = MYSQL.getJdbcUrl();
TestPropertyValues.of("spring.datasource.url=" + mysqlJdbcUrl)
.applyTo(context.getEnvironment());
}
}
Je vais expliquer ce contenu dans l'ordre.
private static final MySQLContainer MYSQL = (MySQLContainer) new MySQLContainer(MYSQL_IMAGE)
.withDatabaseName(DATABASE_NAME)
.withUsername(USERNAME)
.withPassword(PASSWORD)
.withExposedPorts(PORT)
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.withClasspathResourceMapping(INIT_SQL, INIT_SQL_IN_CONTAINER, BindMode.READ_ONLY);
Ici, presque les mêmes paramètres que ceux écrits dans docker-compose.yaml précédemment sont écrits en Java.
Le nom de l'image est spécifié dans le nouveau MySQLContainer (MYSQL_IMAGE), et des paramètres tels que le nom de la base de données, le nom d'utilisateur et le mot de passe sont également définis ici.
De plus, withClasspathResourceMapping monte le SQL pour l'initialisation de la base de données. Il semble que vous puissiez également monter le fichier de configuration MySQL.
static {
MYSQL.start();
}
Je démarre un conteneur MySQL. MySQL.start () doit être exécuté avant MySQL.getJdbUrl () dans initialize décrit ci-dessous.
Je saute cet exemple, mais je pense qu'il vaut mieux appeler MySQL.stop () à la fin.
Étant donné que le port ** de connexion à MySQL démarré par Testcontainers est attribué dynamiquement **, il est nécessaire de définir dynamiquement les informations de connexion en fonction de cela.
J'ai créé une classe appelée MySQLContainerContextInitializer pour modifier dynamiquement les paramètres de Spring Boot.
La méthode de réglage spécifique est la suivante.
@Override
public void initialize(ConfigurableApplicationContext context) {
String mysqlJdbcUrl = MYSQL.getJdbcUrl();
TestPropertyValues.of("spring.datasource.url=" + mysqlJdbcUrl)
.applyTo(context.getEnvironment());
}
En passant, il est nécessaire d'obtenir les informations de connexion à MySQL à partir de l'instance MySQLContainer [SimpleMySQLTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/jdbc-test/src Vous pouvez également voir dans la partie suivante de /test/java/org/testcontainers/junit/SimpleMySQLTest.java).
SimpleMySQLTest.java
@NonNull
protected ResultSet performQuery(MySQLContainer containerRule, String sql) throws SQLException {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(containerRule.getDriverClassName());
hikariConfig.setJdbcUrl(containerRule.getJdbcUrl());
hikariConfig.setUsername(containerRule.getUsername());
hikariConfig.setPassword(containerRule.getPassword());
HikariDataSource ds = new HikariDataSource(hikariConfig);
Statement statement = ds.getConnection().createStatement();
statement.execute(sql);
ResultSet resultSet = statement.getResultSet();
resultSet.next();
return resultSet;
}
Voici le test Spring Boot comme d'habitude.
UserRepositoryImplTest.java
package springdockerexample.infrastructure.user;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import springdockerexample.domain.user.UserRepository;
import springdockerexample.domain.user.Users;
import springdockerexample.testhelper.MySQLContainerContextInitializer;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(initializers = { MySQLContainerContextInitializer.class })
public class UserRepositoryImplTest {
@Autowired
UserRepository userRepository;
@Test
public void test() {
Users users = userRepository.findAll();
assertThat(users.count(), is(2));
}
}
Le but est de spécifier la classe créée précédemment avec @ContextConfiguration (initializers = {SQLContainerContextInitializer.class})
, de démarrer MySQL et de définir les informations de connexion.
Après cela, vous pouvez écrire un test JUnit normal et accéder à la base de données sans aucun problème.
En résumé, ce n'est pas du tout difficile, mais en réalité c'était assez difficile. Cela peut être utile une fois que vous l'avez configuré.
Puisque Testcontainers prend en charge tout conteneur autre que DB, vous pouvez effectuer librement des tests automatiques.
La structure de fichier finale du contenu de cet article est la suivante.
$ tree
.
├── pom.xml
├── ...
└── src
├── main
│ ├── java
│ │ └── springdockerexample
│ │ ├── SpringDockerExampleApplication.java
│ │ ├── ...
│ │ └── infrastructure
│ │ └── user
│ │ ├── UserMapper.java
│ │ └── UserRepositoryImpl.java
│ └── resources
│ ├── application.yaml
│ └── springdockerexample
│ └── infrastructure
│ └── user
│ └── UserMapper.xml
└── test
├── java
│ └── springdockerexample
│ ├── SpringDockerExampleApplicationTests.java
│ ├── infrastructure
│ │ └── user
│ │ └── UserRepositoryImplTest.java
│ └── testhelper
│ └── MySQLContainerContextInitializer.java
└── resources
└── docker-entrypoint-initdb.d
└── init.sql
Le code source est ici.
Recommended Posts