四苦八苦しましたが、SpringBoot3を使用したアプリ開発に関する記事を書いてきました。
(開発環境構築)
(GitHub登録とソース管理)
(簡易画面の作成)
続いて私の中では一番の目的である単体テストについて書いていこうと思います。
その中でも今回は単体テスト環境の構築について書きます。
前提条件
今回作成した単体テストコードは、上記で紹介している記事で構築作成したプログラムに対して実装することを前提としています。まだ、見られていない方はぜひ参考にしていただければと思います。
開発環境
開発環境 | 名称 | 説明 |
---|---|---|
言語 | Java | java 17.0.5 2022-10-18 LTS |
開発ツール | VSCode | Visual Studio Code |
フレームワーク | Spring Boot | バージョン3.0.2 |
データベース | SQL Server(本番用) h2database(単体テスト用) | 単体テスト専用のデータベースはインメモリ |
ビルドツール | Gradle | バージョン7.6 |
テンプレートエンジン | Thymeleaf (タイムリーフ) | Spring BootでWeb画面を作成するときによく使われるテンプレートエンジン |
ORMマッパー | MyBatis | Javaでよく使用されているORマッパー |
CSSフレームワーク | Bootstrap | バージョン5.02 ※今回はCDN形式で使用 |
本番用と単体テスト用のデータベース
本番環境で稼働させているのはSQL Serverなのですが、単体テストのときはテスト環境に依存しないようにH2 Databaseを使用しています。目標としてこの単体テストをGitHubActionsなどの外部の環境で動かしたい思惑があるのでテスト環境に依存しない環境を作る場合は、H2Databaseがおすすめです。
ただし、本番環境(SQLServer)では問題なく動作するSQLが単体テスト環境(H2)ではうまく動かなくなることがあるので注意が必要です。
単体テスト環境の設定
build.gradle(依存関係)の追加設定
今回単体テスト用でpluginsにjacocoを新たに追加しています。Jacocoは記述したプログラムのコードがテストによってどれくらい網羅しているかのカバレッジを出力してくれるライブラリです。
その他単体テストで使用する依存関係としてはruntimeOnly ‘com.h2database:h2’が入っていますがプロジェクト作成時に追加したものなので割愛します。
id ‘jacoco’(5行目)
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
id 'jacoco'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.0'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.0'
}
tasks.named('test') {
useJUnitPlatform()
}
単体テスト用のapplication.properties
本番環境とテスト環境で使用するデータベースが異なるのでsrc\test\resources配下に
application-test.propertiesという名前のファイルを作成します。
単体テストクラスに@ActiveProfiles(“test”)を付与することでテスト用のpropertiesファイルを参照するようにしてくれます。ハイフン移行がプロファイル名になります。
application-test.propertiesには以下のようにDB接続情報を記述します。
server.port = 8084
# DB接続情報
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:sample;MODE=MSSQLServer
spring.datasource.username=sa
spring.datasource.password=
#この設定で起動時にsrc\test\resources配下のdata.sqlとschema.sqlを実行してくれる
spring.sql.init.mode=always
# h2コンソールを利用するための設定
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# SQLSQLログ出力
logging.level.org.springframework=warn
logging.level.com.example.demo.UserDataMapper=debug
・spring.datasource.url=jdbc:h2:mem:sample;MODE=MSSQLServer
※h2Databaseをインメモリで使用するという意味。この設定の場合単体テストが終了するとデータベースが初期化されるようになっているためテストデータの残骸が残らない。
・spring.sql.init.mode=always
※この設定が入っていると起動時に所定のフォルダに格納したSQLを実行してくれる。(後述)
単体テスト用にテーブルとテストデータを作成する
上述しているようにspringBootは、propertiesファイルにspring.sql.init.mode=alwaysを記述するとテストが実行された初期処理でsrc\test\resources配下にschema.sqlとdata.sqlを実行してくれる機能があります。
schema.sqlには、作成するテーブル情報を記載します。複数テーブルが存在する場合は続けて書けばOKです。
-- ユーザーテーブル作成
CREATE TABLE users
(
userid VARCHAR(24) NOT NULL,
name VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
PRIMARY KEY(userid)
);
data.sqlには、初期投入するテストデータを記載します。
-- テストデータ追加
INSERT INTO users(userid, name, email)
VALUES ('test01', 'テスト01', 'test01@example.com');
INSERT INTO users(userid, name, email)
VALUES ('test02', 'テスト02', 'test02@example.com');
テストが実行されると初期処理で上記SQLが実行されます。わざわざ初期データを投入するテストコードを書かなくてもいいので非常に便利です。しかもテスト実行が終わるとインメモリのデータベースなのでデータが削除されるので常に同じ状態のデータベースでテストができます。
単体テストコードを書いてみる
上記設定で単体テストの準備は完了しました。試しに本当にテスト起動時にテーブル作成が行われ、初期データが投入されているかテストをしてみます。
単体テストは基本的にsrc\test\java\com\example\demoは以下に書いていきます。
本来は、1クラスファイルにつき****Tests.javaファイルを作成しテストコードを書きますが今回は動作確認なのでプロジェク作成時に作られているDemoApplicationTests.javaを使って書きます。
このファイルはプロジェクト作成時からひっそりと作成されていますが、正直不要です。最初から記載されているテストコードを削除して以下のコードに書き換えます。
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.boot.test.autoconfigure.MybatisTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import static org.junit.jupiter.api.Assertions.assertEquals;
@MybatisTest
@ActiveProfiles("test")
class DemoApplicationTests {
@Autowired
private UserDao userDao;
@Test
void DB登録確認テスト(){
final User act = userDao.findById("test01");
assertEquals("test01",act.getUserid());
}
}
初期投入用として配置していたdata.sqlのユーザーID:test01が存在するかをテストしています。
INSERT INTO users(userid, name, email)
VALUES ('test01', 'テスト01', 'test01@example.com');
- @Testを記述しないとテストとして認識してくれない。
- @ActiveProfiles(“test”)でapplication-test.propertiesを参照するように切替
VSCode上でテストマークをクリックするとテストのメソッド名が表示されるのでテストの実行を選択します。テストが正常終了すると以下のように緑色になります。
ついでにテスト結果が正しくないときは本当にエラーとなるかを確認してみます。
テストコードのfinal User act = userDao.findById(“test01“);の部分を
final User act = userDao.findById(“test100“);に書き換えて実行します。
エラーの場合は赤くなります。
Jacocoでカバレッジを出力する
最後に依存関係に追加したJacocoでカバレッジを出力してみようと思います。
VsCodeのターミナルで以下のコマンドを実行するだけでOKです。
gradlew build jacocoTestReport
実行結果は以下のフォルダに格納されます。
demo/build/reports/jacoco/test/html/index.html
まとめ
- 単体テストのデータベースはH2などのインメモリを使うと便利
- spring.sql.init.mode=alwaysで初期のテストデータが簡単に投入できる。
- テスト用テーブルはsrc\test\resources\schema.sqlに書く
- テスト用データはsrc\test\resources\data.sqlに書く
- 単体テストはsrc\test\java\com\example\demoに書く
- @Testを記述しないとテストメソッドとして認識してくれない。
- @ActiveProfiles(“test”)でapplication-test.propertiesテスト用に切り替え
仕組みさえ理解できれば結構簡単に単体テストの環境が作れました。SpringBoot(Java)は単体テストのレポート出力機能が豊富なので単体テストをやってみるには最適だと思いました。
次回は本題のcontrollerクラスの単体テストを実装していきたいと思います。
コメント