LoginSignup
11
3

More than 1 year has passed since last update.

TestContainersとSpring Data JDBCを触ってみて自分なりにまとめてみました。

TestContainersとは

TestContainersはデータベース、Selenium Webブラウザー、などのインスタンスを提供します。
TestContainersを使用すると次の種類のテストが簡単になるようです。

  • データアクセスレイヤに対するインテグレーションテスト
    • MySQL、PostgreSQL、Oracleデータベースのコンテナ化されたインスタンスを使用
    • コンテナ化できる他のデータベースも使用可能
  • アプリケーションインテグレーションテスト
    • データベース、メッセージキューなどに依存関係があるアプリケーションレイヤーのテストコードに対するテスト
  • UI/受け入れテスト
    • Seleniumと互換性のあるコンテナ化されたWebブラウザを使用

TestContainersを使うためには(事前準備)

環境

  • Windows 10 Pro
  • Java 11
  • Apache Maven 3.8.2
  • Docker 20.10.11

TestContainersを依存関係に加える

TestContainersを使うための準備です。

Dockerの要件をみて、version等に問題ないか確認します。
https://www.testcontainers.org/supported_docker_environment/

windowsの場合はこちらも確認しといた方がよさそうです。
https://www.testcontainers.org/supported_docker_environment/windows/

データベースはPostgreSQLを使用してみます。
https://www.testcontainers.org/modules/databases/postgres/

テストフレームワークはJUnit5を使用することにします。

JUnit5を使用する場合には、junit-jupiterを追加する必要があります。

なので、pomはこんな感じ。

pom.xml

<dependency>
    <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</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>

SpringBootの依存関係

SpringBoot 2.6.1を使用します。

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.1</version>
    <relativePath/> 
</parent>

データベースアクセスにはSpring Data JDBCを使います。
追加したライブラリはこんな感じ。

pom.xml

  <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
  </dependency>
  <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
  </dependency>

Spring Data JDBCを使用したデータベースアクセス

接続設定

application.propertiesにはPostgreSQLへ接続するための設定情報を記述していきます。

後述しますが、spring.datasource.urlは記述していないです。
テストコード実行時に接続先のコンテナのURLを動的に割り当てます。

また今回はSpringBootのテーブルの初期化機能を使います。

schema.sqlやdata.sqlを用意し、DDLを記述します。
spring.sql.init.mode=alwaysとしています。

詳しくはこのあたり
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.data-initialization.using-basic-sql-scripts

application.properties

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=username
spring.datasource.password=password
spring.sql.init.mode=always

次にDDLを記述します。(insert文も書いています。)

schema.sql

create table stock
(
    id       integer,
    name     text,
    quantity integer,
    primary key (id)
);

insert into stock(id, name, quantity)
values (1, 'りんご', 5);



Spring Data JDBC

リファレンスはこのあたり。

サンプルはこのあたり。
https://github.com/spring-projects/spring-data-examples

テーブルに対応するオブジェクトです。

lombokを使用しています。

主キーにはSpringのアノテーション@Idを付与しておきます。


@AllArgsConstructor
@Data
public class Stock {
    @Id
    private Integer id;
    private String name;
    private Integer quantity;
}

上記のオブジェクトを操作するために、

CrudRepositoryを継承したRepositoryインタフェースを作成します。

CrudRepositoryを継承するだけで簡易なCRUD機能が提供されるので今回は継承するだけでもいいですが、
テキトーな自分で書いたSQLのメソッドも書いておきます。

public interface StockRepository extends CrudRepository<Stock, Integer> {

    @Query("update stock set quantity=:quantity where id=:id")
    Integer update(@Param("id") Integer id, @Param("quantity") Integer quantity);

}

動作確認

準備

それではテストコードを書いて動かしてみます。

まず以下のようなテストクラスを作ります。


@Testcontainers
@SpringBootTest
public class PostgresqlContainerTest {
    @Container
    private static final PostgreSQLContainer<?> postgres =
            new PostgreSQLContainer<>(DockerImageName.parse("postgres:12"))
                    .withUsername("username")
                    .withPassword("password")
                    .withDatabaseName("postgres")

    @Autowired
    StockRepository repository;

    @DynamicPropertySource
    static void setup(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
    }
}

をみると、JupiterもしくはJUnit5を使用する場合、テストクラスには@Testcontainerを付ける必要がありそうです。

DockerImageName.parse()の引数にはイメージ名を指定します。
イメージにはPostgreSQLのversion12を指定しました。

@Containerをつけた変数にはstaticで定義していますが、この場合にはテストメソッドが実行される前に開始されて最後のメソッドで停止されます。
その結果、テストメソッド間で共有されます。

staticフィールドでなければ、メソッド実行されるたびに開始されるようです。

@DynamicPropertyRegistryを使って、起動中のコンテナへ接続するための情報を設定します。

実行

それでは最後に実行してみます。


    @Test
    void test() {
        Assertions.assertThat(postgres.isRunning()).isTrue();

        final Optional<Stock> stock = repository.findById(1);
        Assertions.assertThat(stock.get()).isEqualTo(new Stock(1, "りんご", 5));

        final Integer update = repository.update(1, 20);
        Assertions.assertThat(update).isEqualTo(1);

    }

まとめ

TestContainersを使用してテストコードの記述までやってみました。

h2でも統合テストはできますが一部のSQLが実行できない、なんてこともあると思うので
それに比べると便利だと思います。

ただ、テストの実行がやや重い。。

おわり。

11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3