🐳

TestContainers を使ってR2DBCのテストを作成する

2023/02/07に公開

TestContainers はR2DBCもサポートしていますが、SQLServerとの組み合わせはなかなか情報なく、とりあえず動くところまできたので、まとめておきます。
R2DBC support

環境

  • Windows11

  • Docker Desktop

  • Java17

  • SpringBoot 2.7

  • SQLServer 2019

  • Maven

  • TestContainers

準備するもの

pom.xml

まずはpom.xmlから抜粋。省略してますが、lombok使ってます。

pom.xml
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath />
    </parent>

    <properties>
        <testcontainer.version>1.17.6</testcontainer.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
        </dependency>
        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-mssql</artifactId>
            <version>1.0.0.RELEASE</version>
            <scope>runtime</scope>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>r2dbc</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>mssqlserver</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>11.2.3.jre17</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainer.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

bom使ってますが、使わない場合は org.testcontainers:testcontainers をdependencyに追加すれば良さそうです。
サイトやDBによってdependencyに追加するライブラリの組み合わせが違うので、なかなかエラーが解消できませんでしたが、SQLServerの場合だと、この組み合わせでできました。多いな。。

application-test.yml

DBへの接続、初期化、おまけでログの設定を記述します。
TC_IMAGE_TAG に microsoft-mssql-server にあるdocker tag を記載します。
spring.sql.init.mode : always はこれを記述しないとschema.sqlが読み込まれなかったので、書いてます。デフォルトで呼ばれるはずなんだけどな?

application-test.yml
spring:
  r2dbc:
    url: r2dbc:tc:sqlserver:///?TC_IMAGE_TAG=2019-latest

  sql:
    init:
      mode: always  # schema.sql を読ませるために設定

logging:
  level:
    io.r2dbc.mssql.QUERY: DEBUG # for queries
    io.r2dbc.mssql.PARAM: DEBUG # for parameters
    org.springframework.r2dbc: DEBUG

container-license-acceptance.txt

SQLServerなどライセンスが必要なコンテナを使う場合は、これが必要なようです。
src/test/resources/ の下に配置します。

Due to licencing restrictions you are required to accept an EULA for this container image. To indicate that you accept the MS SQL Server image EULA, call the acceptLicense() method, or place a file at the root of the classpath named container-license-acceptance.txt, e.g. at src/test/resources/container-license-acceptance.txt. This file should contain the line: mcr.microsoft.com/mssql/server:2017-CU12 (or, if you are overriding the docker image name/tag, update accordingly).

Please see the microsoft-mssql-server image documentation for a link to the EULA document.

MS SQL Server Module

container-license-acceptance.txt
mcr.microsoft.com/mssql/server:2019-latest  // 使用するコンテナイメージのタグを指定する

※複数のコンテナを使う場合はこのファイルにコンテナイメージを追記していくみたいです。

schema.sql

src/test/resources の配下においておくと、Springが起動時に読んでくれます。確か。
今回は簡単なCompanyテーブルを作っておきます。

schema.sql
CREATE TABLE [dbo].[Company](
	[Id] [int] IDENTITY(1, 1) NOT NULL,
	[CompanyName] [nvarchar](511) NOT NULL,
	CONSTRAINT [PK_Company] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (
		PAD_INDEX = OFF,
		STATISTICS_NORECOMPUTE = OFF,
		IGNORE_DUP_KEY = OFF,
		ALLOW_ROW_LOCKS = ON,
		ALLOW_PAGE_LOCKS = ON,
		OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF
	) ON [PRIMARY]
) ON [PRIMARY];

Entity/Repository

Company とマッピングさせるEntityとRepositoryです。

Company.java
@AllArgsConstructor
@NoArgsConstructor
@Data
@Table("Company")
public class Company implements Serializable {

    private static final long serialVersionUID = 1L;

    @GeneratedValue
    @Id
    @Column("Id")
    private Integer id;

    @Column("CompanyName")
    private String companyName;

}

CompanyRepository.java
public interface CompanyRepository extends ReactiveCrudRepository<Company, Integer> {

}

テストコード

さて、ようやく準備が整ったのでテストコードです。
ちなみに、data.sqlもresourcesのルートに配置しておくと、データをinsertしてくれますが、SQLエラーが解消できなかったので、今回は諦めて、テストコードでデータを投入してます。
解決できたら追記するかも。

CompanyRepositoryTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@DataR2dbcTest
@ActiveProfiles("test")
class CompanyRepositoryTest {

    @Autowired
    private CompanyRepository companyRepository;

    @Test
    void testFindById() {
        // data.sql でエラーにどうしてもなるので、仕方なくsaveする
        var c = new Company();
        c.setCompanyName("株式会社丸さんかく");

        companyRepository.save(c).subscribe();

        var actual = companyRepository.findOneById(1).block();
        assertEquals(c.getCompanyName(), actual.getCompanyName());
    }

}

テスト自体はあんまり意味ないですが、データ取得の確認はこんな感じ。
@DataR2dbcTest で読み込むレイヤーを絞ってるので、少しは速く起動できるんじゃないかなと思ったり。

それでは、良きテストライフを。

参考

リファレンスドキュメント

Discussion