testcontainersの使い方 〜Redis編〜

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を更新する
・他は単体テストと同じ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です