Redisに依存したアプリケーションのテストを、
実際にRedisと接続してテストしたいケースがあるかと思います。
そんなときに、「testcontainers」を使うとテスト時に一時的に起動するRedisを用いてテストすることができます!
この記事では、testcontainersを使ってRedis周りの単体テスト・結合テストを実装する方法をまとめます。
実行環境
ビルドツールは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-redis'
implementation 'org.springframework.boot:spring-boot-starter-web'
// https://mvnrepository.com/artifact/redis.clients/jedis
testImplementation 'redis.clients:jedis:4.3.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:testcontainers'
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 Redis (Access+Driver)
テスト対象のクラス
まずテスト対象のクラスを実装します。
@Component
@RequiredArgsConstructor
public class RedisClient {
private final RedisTemplate<String, String> redisTemplate;
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
}
単体テスト
では先ほどのRedisClient
の単体テストを実装してみます。
@Testcontainers
class RedisClientTest {
// テストコンテナを生成(裏でDockerコンテナが起動する)
@Container
public static GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine"))
.withExposedPorts(6379);
private RedisClient redisClient;
@BeforeEach
void setup() {
// 起動したコンテナの情報を元にRedisTemplateを生成
var redisConfiguration = new RedisStandaloneConfiguration(redis.getHost(), redis.getMappedPort(6379));
var jedisConnectionFactory = new JedisConnectionFactory(redisConfiguration);
jedisConnectionFactory.afterPropertiesSet();
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.afterPropertiesSet();
redisClient = new RedisClient(redisTemplate);
}
@Test
void test() {
redisClient.set("sample", "sample");
var actual = redisClient.get("sample");
var expected = "sample";
assertThat(actual).isEqualTo(expected);
}
}
ポイント
・@Testcontainersが@BeforeAll, @AfterAllでコンテナの開始と終了を管理してくれる・@Containerは@Testcontainersに管理対象のコンテナを知らせる
・接続情報もGenericContainerのインスタンスから取得
結合テスト
次に結合テストです。
本当はControllerやServiceクラスを作ってEnd to Endでやりたいのですが、RedisClient
をAutowiredして使う方針で簡素化。。。
@Testcontainers
@SpringBootTest
class ApplicationTest {
@Autowired
private RedisClient redisClient;
// テストコンテナを生成(裏でDockerコンテナが起動する)
@Container
public static GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine"))
.withExposedPorts(6379);
// propertiesを更新
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", () -> redis.getHost());
registry.add("spring.data.redis.port", () -> redis.getMappedPort(6379));
}
@Test
void test() {
redisClient.set("sample", "sample");
var actual = redisClient.get("sample");
var expected = "sample";
assertThat(actual).isEqualTo(expected);
}
}
ポイント
・@DynamicPropertySourceを付与したメソッドで起動したコンテナの情報をもとにpropertyを更新する・他は単体テストと同じ