[JAVA] Starten Sie den MySQL-Container nur während des Spring Boot + MyBatis-Tests mit Testcontainern

Einführung

Sie können Testcontainer (https://www.testcontainers.org/) verwenden, um einen MySQL-Container nur beim Testen von JUnit zu starten.

Beispielcode SimpleMySQLTest.java Es scheint recht einfach zu bedienen zu sein, aber ich hatte große Probleme, als ich es mit Spring Boot + MyBatis ausprobierte, daher werde ich zusammenfassen, wie es funktioniert.

Führen Sie zunächst Spring Boot + MyBatis normal aus

Schreiben Sie vor dem Testen mit Testcontainern einen Arbeitscode.

Erstellen Sie Code für MyBatis

Erstellen Sie drei Dateien: UserRepositoryImpl.java, UserMapper.java und 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>

Bereiten Sie die Eigenschaftendatei vor

Schreiben Sie die DB-Verbindungsinformationen in application.yaml.

application.yaml


spring:
  datasource:
    url: jdbc:mysql://localhost/mydb
    username: user
    password: password
    driverClassName: com.mysql.cj.jdbc.Driver

Bereiten Sie die Testdaten vor

Bereiten Sie die Daten für die Entwicklung und das Testen in einer Datei vor.

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);

Container lokal starten

Starten Sie MySQL mit Docker Compose, um den Betrieb von Spring Boot + MyBatis zu überprüfen.

Zu diesem Zeitpunkt werden die Daten automatisch eingefügt, indem die zuvor in docker-entrypoint-initdb.d vorbereitete SQL-Datei gemountet wird.

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

Funktionsprüfung

Wenn Sie spring-dev-tools einschließen, beginnt es mit ./mvnw spring-boot: run.

$ ./mvnw spring-boot:run
$ curl localhost:8080/users
{"users":[{"name":"Alice","age":20},{"name":"Bob","age":30}]}

Es funktioniert sicher.

Auf diese Weise ist es möglich, mit einem Container zu testen, der mit Docker Compose gestartet wurde, aber es ist nicht möglich, den Container für jeden Testfall neu zu starten.

Verwenden Sie Testcontainer, um Container während des JUnit-Tests nach Belieben zu starten.

Starten Sie den Container nur, während JUnit in Testcontainern ausgeführt wird

Starten Sie MySQL zum Testen und Verbinden der Informationseinstellungen

Informationen zum Testen finden Sie unter "Einweg-Datenbankcontainer mit Testcontainern vorbereiten und die Spring Boot-Anwendung testen". Erstellen Sie die folgenden Dateien, um den MySQL-Container zu starten und die Verbindungsinformationen auf den Kontext zu setzen.

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());
  }
}

Ich werde diesen Inhalt der Reihe nach erklären.

MySQL-Containereinstellungen

  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);

Hier werden fast die gleichen Einstellungen wie in docker-compose.yaml zuvor in Java geschrieben.

Der Bildname wird im neuen MySQLContainer (MYSQL_IMAGE) angegeben. Hier werden auch Einstellungen wie Datenbankname, Benutzername und Kennwort vorgenommen.

Außerdem stellt withClasspathResourceMapping die SQL für die DB-Initialisierung bereit. Es scheint, dass Sie auch die MySQL-Konfigurationsdatei mounten können.

Starten Sie den MySQL-Container

  static  {
    MYSQL.start();
  }

Ich starte einen MySQL-Container. MySQL.start () muss vor MySQL.getJdbUrl () in der unten beschriebenen Initialisierung ausgeführt werden.

Ich überspringe dieses Beispiel, aber ich denke, es ist besser, am Ende MySQL.stop () aufzurufen.

Einstellungen für Verbindungsinformationen

Da der von Testcontainern gestartete ** Port für die Verbindung zu MySQL dynamisch zugewiesen wird **, müssen die darauf basierenden Verbindungsinformationen dynamisch festgelegt werden.

Ich habe eine Klasse namens MySQLContainerContextInitializer erstellt, um die Spring Boot-Einstellungen dynamisch zu ändern.

Die spezifische Einstellmethode ist wie folgt.

  @Override
  public void initialize(ConfigurableApplicationContext context) {
    String mysqlJdbcUrl = MYSQL.getJdbcUrl();
    TestPropertyValues.of("spring.datasource.url=" + mysqlJdbcUrl)
            .applyTo(context.getEnvironment());
  }

Die Verbindungsinformationen zu MySQL von der MySQLContainer-Instanz müssen übrigens SimpleMySQLTest.java abgerufen werden. Sie können auch aus dem folgenden Teil von /test/java/org/testcontainers/junit/SimpleMySQLTest.java) sehen.

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

Testbeschreibung

Hier ist der Spring Boot Test wie gewohnt.

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));
  }
}

Der Punkt besteht darin, die zuvor mit "@ContextConfiguration (initializers = {SQLContainerContextInitializer.class})" erstellte Klasse anzugeben, MySQL zu starten und die Verbindungsinformationen festzulegen.

Danach können Sie einen normalen JUnit-Test schreiben und problemlos auf die Datenbank zugreifen.

Zusammenfassung

Zusammenfassend ist es überhaupt nicht schwierig, aber in Wirklichkeit war es ziemlich schwierig. Dies kann nützlich sein, wenn Sie es eingerichtet haben.

Da Testcontainer jeden anderen Container als DB unterstützen, können Sie automatisch automatische Tests durchführen.

Die endgültige Dateistruktur des Inhalts dieses Artikels lautet wie folgt.

$ 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

Der Quellcode lautet hier.

Recommended Posts

Starten Sie den MySQL-Container nur während des Spring Boot + MyBatis-Tests mit Testcontainern
Führen Sie einen Transaktionsbestätigungstest mit Spring Boot durch
Starten Sie die Entwicklung von Webanwendungen mit Spring Boot
Implementieren Sie CRUD mit Spring Boot + Thymeleaf + MySQL
Formularklassenvalidierungstest mit Spring Boot
Testen Sie den Controller mit Mock MVC im Spring Boot
Asynchrone Verarbeitung mit regelmäßiger Ausführung in Spring Boot
Bis zur Datenerfassung mit Spring Boot + MyBatis + PostgreSQL
Verwendung von MyBatis2 (iBatis) mit Spring Boot 1.4 (Spring 4)
Erstellen Sie eine CRUD-App mit Spring Boot 2 + Thymeleaf + MyBatis
Einstellungen für die Verbindung zu MySQL mit Spring Boot + Spring JDBC
Versuchen Sie es mit einem DI-Container mit Laravel und Spring Boot
[JUnit 5-kompatibel] Schreiben Sie einen Test mit JUnit 5 mit Spring Boot 2.2, 2.3
[JUnit 5] Schreiben Sie einen Validierungstest mit Spring Boot! [Parametrisierungstest]
Testen Sie die Klasse mit Feldinjektion im Spring-Boot-Test, ohne den Spring-Container zu verwenden
Bis Sie mit der Entwicklung mit Spring Boot in Eclipse 1 beginnen
Bis Sie mit der Entwicklung mit Spring Boot in Eclipse 2 beginnen
Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben
Mit Spring Boot herunterladen
Generieren Sie mit Spring Boot einen Barcode
Implementieren Sie GraphQL mit Spring Boot
Beginnen Sie mit Spring Boot
Hallo Welt mit Spring Boot!
Führen Sie LIFF mit Spring Boot aus
SNS-Login mit Spring Boot
Datei-Upload mit Spring Boot
Spring Boot beginnt mit dem Kopieren
Spring Boot beginnend mit Docker
Setzen Sie Cookies mit Spring Boot
Verwenden Sie Spring JDBC mit Spring Boot
Modul mit Spring Boot hinzufügen
Erste Schritte mit Spring Boot
Mail mit Spring Boot verschicken
Implementieren Sie einen einfachen Web-REST-API-Server mit Spring Boot + MySQL
02. Ich habe eine API erstellt, um eine Verbindung von Spring Boot zu MySQL (My Batis) herzustellen.
Beispielcode zum Testen eines Spring Boot-Controllers mit MockMvc
Stellen Sie sich die Spring Boot-App mit dem Jib-Maven-Plugin vor und starten Sie sie mit Docker