😽

Playwright + JUnit によるE2Eテストの自動化(Spring向け)

に公開

概要

E2Eテストの定番ライブラリといえばPlaywrightですが、DBの状態をテストケースごとに変更するための機能はありません。
この記事では、Java用のPlaywrightとJUnit(+Spring Test DBUnit)を用いて、
テストケースごとにデータベースの状態を切り替えながら自動でE2Eテストを行う方法を紹介します。
なお、Springでバックエンドを構築していることを前提とします。

依存関係

以下をpom.xmlに追加します。

...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.github.springtestdbunit</groupId>
    <artifactId>spring-test-dbunit</artifactId>
    <version>1.3.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.microsoft.playwright</groupId>
    <artifactId>playwright</artifactId>
    <version>1.52.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.7.3</version>
    <scope>test</scope>
</dependency>
...

gradleの場合は、build.gradleに以下を追加してください。

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'com.github.springtestdbunit:spring-test-dbunit:1.3.0'
    testImplementation 'com.microsoft.playwright:playwright:1.52.0'
    testImplementation 'org.dbunit:dbunit:2.7.3'
}

なお、ライブラリのバージョンは 2025/09/29 時点で筆者が使用しているものです。
読者の方は自分の置かれている状況に応じて適切にバージョンを変換してください。

書き方

書き方は普通の単体テストと大きく変わりません。
JUnitを使った単体テストの書き方がわからない場合は以下を読んでください。
https://zenn.dev/muit_techblog/articles/6668eb6bc88d0a

Playwright(Java)の基本については以下を読んでください
https://zenn.dev/izumi_ren/articles/aa3aa432f33b69

ここでは、普通のテストとは異なる点、気を付けるべき点を中心に解説します。

@SpringBootTest

SpringBootTestアノテーションを付けると、SpringBootの機能が使えます。(application.propertiesとかを読んでくれる)
デフォルトではアプリを起動しないのですが、以下のようにすることで起動できます。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = DummyApplication.class)

以下で各引数の説明をします。

webEnvironment

SpringBootTest.WebEnvironment.DEFINED_PORTを設定することで、application.propertiesで指定したポートを使うようになります。フロントエンドとの連携を確認したい等、決まったポートで起動したい場合はこれを使いましょう。

SpringBootTest.WebEnvironment.RANDOM_PORTを設定することで、ランダムなポートを使うようになります。何でもいい場合はこれを使いましょう。

classes

classesパラメータは、テスト時に使用するSpring設定クラス(Configuration)を明示的に指定するために使用されます。
例えば以下のようなメインクラスがあってそれをテスト実行時のSpring Contextの起点にしたい場合、

@SpringBootApplication
public class DummyApplication {
    public static void main(String[] args) {
        SpringApplication.run(TeacherApplication.class, args);
    }
}

classes = DummyApplication.class と設定します。

明示的に指定しない場合は、@SpringBootApplicationを以下の順序で探します。

  1. テストクラスと同じパッケージ内
  2. 上位パッケージ

明示的に示しておいた方が僕は好みです。確実なので

TestExecutionListeners

@TestExecutionListenersは、Springテストフレームワークにおいて、テストのライフサイクル管理を行うためのアノテーションです。テストの実行前後で特定の処理を自動実行するリスナーを設定できます。

数あるリスナーの中でも、今回はDbUnitTestExecutionListenerを使います。
これは、SpringTestとDbUnitを連携させるためのテストリスナーで、テストの実行前後でデータベースのセットアップや検証を自動的にやってくれます。

データベースのセットアップ

Configuration

DBUnitの設定を行うために、DbUnitConfigを src/test 配下に置きます。

DbUnitConfig.java

@Configuration
@Profile("test")
public class DbUnitConfig {
    private String schema = "dbo";

    @Bean
    public DatabaseConfigBean dbUnitDatabaseConfig() {
        DatabaseConfigBean bean = new DatabaseConfigBean();
        bean.setAllowEmptyFields(true);
        return bean;
    }

    @Bean
    public DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection(
            DataSource dataSource, DatabaseConfigBean dbUnitDatabaseConfig) {
        DatabaseDataSourceConnectionFactoryBean bean = new DatabaseDataSourceConnectionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setDatabaseConfig(dbUnitDatabaseConfig);
        bean.setSchema(schema);
        return bean;
    }

}

どのスキーマにインプットデータをインサートするかはschema変数に依存します。
dbo以外のスキーマにインサートする場合はschemaを変更してください。

@DatabaseSetup

Spring Test DBUnit では、データベースにインプットするデータをアノテーション(@DatabaseSetup)中のvalueで指定します。

例:

@DatabaseSetup(value = "tc001/input/")

なお、value中のパスのルートは src/test/resources です。

インプットデータ

インプットするデータは、valueで指定したフォルダに{テーブル名}.csvの形式で設置します。

product.csv

"product_id","product_name","product_type"
"0000000001","商品1","商品"
"0000000002","商品2","サービス"

user.csv

"user_id","user_name","user_type"
"0000000001","テストユーザー","テストユーザー"
"0000000002","テストユーザー2","テストユーザー2"

table-ordering.txt

product
user

table-ordering.txtは、各インプットデータを挿入する順番を指定できます。
この例では、productテーブル、userテーブルの順でインプットデータを挿入します。

なお、特に設定を変更しなければデフォルトでクリーンインサートが選択されます。
クリーンインサートとは、テーブルの内容をデリートしてからインサートを行うことを指します。
ですので、元々product、userにあったデータは消えることになります。注意してください。

※余談:インサートはtable-ordering.txtの順で行われますが、デリートは逆順です。外部キー制約のあるテーブルにデータを挿入する際に沼りがちな仕様なので、覚えておいて損は無いです。

@DbUnitConfiguration

Spring Test DbUnitは、デフォルトでは準備データをxmlで読み込むようになっています。
CSVファイルを読み込ませるためにはテストクラスに以下のアノテーションを付ける必要があります。

@DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class)

※Microsoft SQL Serverを使う場合

SQL Serverを使う場合は、もう一つ引数を追加することをお勧めします。

@DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class, databaseOperationLookup = MicrosoftSqlDatabaseOperationLookup.class)

MicrosoftSqlDatabaseOperationLookup.classを設定することで、identityのカラムにも明示的な値をインサートできるようになります。

上記踏まえて、テストの実装例を記述します。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = TeacherApplication.class)
@ActiveProfiles("it")
@TestExecutionListeners({
    DbUnitTestExecutionListener.class
})
public class ArticleTest extends BaseTest {

    @Nested
    @DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class, databaseOperationLookup = MicrosoftSqlDatabaseOperationLookup.class)
    public class InitialDisplayTest {

        @Test
        @DatabaseSetup(value = "tc001/")
        void tc001_正常系_診断一覧表示_データ有り() throws InterruptedException {
            // ダミーログインで認証
            DummyLoginPage dummyLoginPage = new DummyLoginPage(page);
            dummyLoginPage.inputId("123456789");
            dummyLoginPage.inputPassword("password");
            dummyLoginPage.clickLoginButton();

            // 商品一覧ページに遷移
            ProductListPage productListPage = new ProductListPage(page);
            productListPage.navigate();
            productListPage.waitForPageLoad();

            // 期待結果の確認
            assertTrue(productListPage.isPageTitleVisible());
            assertTrue(productListPage.isHelpTextVisible());
        }

        @Test
        @DatabaseSetup(value = "tc002/")
        void tc002_正常系_診断一覧表示_データ無し() throws InterruptedException {
            // ダミーログインで認証
            DummyLoginPage dummyLoginPage = new DummyLoginPage(page);
            dummyLoginPage.inputId("123456789");
            dummyLoginPage.inputPassword("password");
            dummyLoginPage.clickLoginButton();

            // 診断テスト一覧ページに遷移
            ProductListPage productListPage = new ProductListPage(page);
            productListPage.navigate();
            productListPage.waitForPageLoad();

            // 期待結果の確認
            assertTrue(productListPage.isPageTitleVisible());
            assertTrue(productListPage.isCreateNewButtonEnabledVisible());
        }
    }
}

参考

https://springtestdbunit.github.io/spring-test-dbunit/

関連記事

aShot で簡単にVRTを行う:https://zenn.dev/izumi_ren/articles/f5e166a9b3e7df

Discussion