Cassandraに依存したアプリケーションのテストを、
実際にCassandraと接続してテストしたいケースがあるかと思います。
そんなときに、「testcontainers」を使うとテスト時に一時的に起動するCassandraを用いてテストすることができます!
この記事では、testcontainersを使ってCassandra周りの単体テスト・結合テストを実装する方法をまとめます。
実行環境
ビルドツールはGradleを使います。
build.gradleは以下の通りです。
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.6'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'hirabay'
version = '0.0.1'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
set('testcontainersVersion', "1.18.0")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-cassandra'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:cassandra'
testImplementation 'org.testcontainers:junit-jupiter'
}
dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
メモ
start.spring.io で以下の依存を選択すると同様のbuild.gradleが生成されます!・Testcontainers
・Spring Data for Apache Cassandra
テスト対象のクラス
まずテスト対象のクラスを実装します。
@Component
@RequiredArgsConstructor
public class CassandraClient {
private final CassandraTemplate cassandraTemplate;
public List<Sample> findAll() {
return cassandraTemplate.select("""
SELECT *
FROM sample.t_sample
""", Sample.class);
}
}
@Data
@Table("t_sample")
public class Sample {
@PrimaryKey
private String key;
private String value;
}
CassandraTemplateを使ってCQLを実行するシンプルな作りです!
単体テスト
では先ほどのCassandraClient
の単体テストを実装してみます。
@Slf4j
@Testcontainers
class CassandraClientTest {
// テストコンテナを生成(裏でDockerコンテナが起動する)
@Container
public static CassandraContainer<?> cassandra = new CassandraContainer<>("cassandra:3.11.2")
.withInitScript("initial.cql"); // cassandraの初期化
private CassandraClient cassandraClient;
@BeforeEach
void setup() {
// 起動したコンテナの情報を元にsessionを生成
CqlSession cqlSession = CqlSession.builder()
.addContactPoint(cassandra.getContactPoint())
.withLocalDatacenter(cassandra.getLocalDatacenter())
.build();
var template = new CassandraTemplate(cqlSession);
cassandraClient = new CassandraClient(template);
}
@Test
void test() {
var actual = cassandraClient.findAll();
var expected = new Sample();
expected.setKey("key1");
expected.setValue("value1");
assertThat(actual).isEqualTo(Collections.singletonList(expected));
}
}
CREATE KEYSPACE sample
WITH REPLICATION = {'class':'SimpleStrategy','replication_factor':1};
CREATE TABLE sample.t_sample (key text PRIMARY KEY, value text);
INSERT INTO sample.t_sample (key, value) VALUES ('key1', 'value1');
ポイント
・@Testcontainersが@BeforeAll, @AfterAllでコンテナの開始と終了を管理してくれる・@Containerは@Testcontainersに管理対象のコンテナを知らせる
・CassandraContainer#withInitScriptを実行することでデータベースを初期化
・接続情報もCassandraContainerのインスタンスから取得
結合テスト
次に結合テストです。
本当はControllerやSserviceクラスを作ってEnd to Endでやりたいのですが、CassandraClient
をAutowiredして使う方針で簡素化。。。
@Testcontainers
@SpringBootTest
class ApplicationTest {
@Autowired
private CassandraClient cassandraClient;
// テストコンテナを生成(裏でDockerコンテナが起動する)
@Container
public static CassandraContainer<?> cassandra = new CassandraContainer<>("cassandra:3.11.2")
.withInitScript("initial.cql"); // 初期化
// propertiesを更新
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.cassandra.keyspace-name", () -> "sample");
var contactPoint = "%s:%d".formatted(cassandra.getContactPoint().getHostName(), cassandra.getContactPoint().getPort());
registry.add("spring.cassandra.contact-points", () -> contactPoint);
registry.add("spring.cassandra.local-datacenter", () -> cassandra.getLocalDatacenter());
}
@Test
void test() {
var actual = cassandraClient.findAll();
var expected = new Sample();
expected.setKey("key1");
expected.setValue("value1");
assertThat(actual).isEqualTo(Collections.singletonList(expected));
}
}
ポイント
・@DynamicPropertySourceを付与したメソッドで起動したコンテナの情報をもとにpropertyを更新する・他は単体テストと同じ