[Spring boot] Test Containers를 활용한 Redis & DynamoDB Test 환경 구축

Byuk_mm·2022년 9월 11일
0

Spring Boot Development

목록 보기
7/13
post-thumbnail

Spring Boot 개발 중 학습이 필요한 내용을 정리하고,
트러블 슈팅 과정을 기록하는 포스팅입니다.




✅ Background


이번 프로젝트에 RedisDynamoDB를 활용하고 있습니다.
RedisDynamoDB를 활용한 Test 환경을 구축하는 것에서 많은 고민이 들었습니다.

처음으로 생각한 방법은 Test용으로 모든 시스템을 카피해서 생성하는 것이었습니다.

RedisEC2를 하나 새로 파서 Test용으로만 올려 놓아서 연결하고,
DynamoDB 또한 Test으로 테이블을 생성하여 연결하는 방법이었습니다.

하지만, 이렇게 되면 cost 측면에서도 손해인 것 같았으며,
각각의 시스템 config의 수정 사항이 있을 때 수정이 상당히 번거롭기 때문에 생산성이 떨어집니다.

때문에 해당 부분에서 대해서 검색을 하면서 큰 도움을 받을 수 있는 티스토리를 찾았습니다.

해당 글에서는 Redis 테스트 환경 설정에 관한 방법을 말하고 있습니다.
(이 티스토리에 들어가면 더 자세한 내용을 볼 수있습니다.)

  1. 처음으로 내가 생각한 것과 비슷하게, 로컬에 Redis를 생성해서 사용하는 방법이 있습니다.
    -> 이 방법은 앞서 말했듯, 매번 직접 설치하고 설정해야하기 때문에 생산성이 떨어집니다.

  2. 두번째 방법은 Embedded Redis 라이브러리를 사용하는 방법입니다. Container 기술 이전에는 이 방법이 최선의 방법이었지만, 더 이상은 아니라고 합니다.
    Container에 비해 덜 경량화된 방식일 뿐더러, 실제로 해당 라이브러리의 오픈 소스 활동이 중단됐다고 합니다.

  3. 마지막으로 오늘의 주제인 TestContainers 라이브러리를 활용하는 것입니다.
    TestContainers 는 컨테이너를 기반으로 동작하여 앞서 말한 문제점들을 획기적으로 해결합니다. TestContainers 에 대해서 조금 더 알아 보겠습니다.



✅ TestContainers


TestContainers는 Docker Container를 활용한 일회용 인스턴스를 제공하는 JUnit 테스트 라이브러리를 말합니다. 쉽게 말해서 테스트를 위해 Docker Container 를 실행시켜주는 자바 라이브러리입니다.

TestContainers를 활용하면 단순히 테스트 실행만으로 특정 컨테이너가 실행되어 테스트를 진행하고 모든 테스트가 끝나면 컨테이너도 자동으로 종료됩니다. 컨테이너로 동작하기 때문에 어느 환경에서든 바로 실행이 가능합니다.




✅ TestContainers Redis & DynamoDB


TestContainers 라이브러리를 활용해서 RedisDynamoDB 컨테이너를 띄운 다음에 Test 환경에서 사용할 수 있도록 ContainerBaseTest 클래스를 만들어보겠습니다.

📌 IntegrationTest

우선 ContainerBaseTest 클래스는 아래의 IntegrationTest를 상속받아서 정의했습니다.
IntegrationTest는 주로 Controller 계층의 Test에 활용되는 통합 테스트 설정 클래스입니다.

@SpringBootTest
@Disabled
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Transactional
public class IntegrationTest {

    @Autowired
    protected MockMvc mvc;

    @Autowired
    protected ObjectMapper objectMapper;

}

📌 ContainerBaseTest

ContainerBaseTest 클래스는 위의 IntegrationTest를 상속받아 정의됩니다.

이 후에는 TestContainers를 기반으로 테스팅 돼야 하는 클래스에서 이 ContainerBaseTest 클래스를 상속받아서 실행하게 되면,
RedisDynamoDB 컨테이너가 자동 생성되며, 테스트 종료시 자동 삭제됩니다.

아래와 같이 redisdynamodbdocker images 값을 설정합니다.
withExposedPorts 메소드를 통해 각각 컨테이너의 포트를 명시적으로 노출합니다.
withReuse 메소드 설정을 통해 컨테이너를 재사용할 수 있도록 합니다.

@DynamicPropertySource 애노테이션 설정을 통해 실행된 컨테이너의 host, port 값을 가져와 동적으로 application에 설정 값을 매핑합니다.

참고 티스토리

@Testcontainers
public class ContainerBaseTest extends IntegrationTest {

    // 로컬의 도커 데스크탑이 실행 중에 있어야 한다!

	// redis docker image
    private static final String DOCKER_REDIS_IMAGE = "redis:6-alpine";

	// dynamodb docker image
    private static final String DOCKER_DYNAMODB_IMAGE = "amazon/dynamodb-local:latest";

    @ClassRule
    static final GenericContainer REDIS_CONTAINER;

    @ClassRule
    public static GenericContainer DYNAMODB_CONTAINER;

    static {

        REDIS_CONTAINER = new GenericContainer<>(DOCKER_REDIS_IMAGE)
                .withExposedPorts(6379)
                .withReuse(true);

        DYNAMODB_CONTAINER = new GenericContainer<>(DOCKER_DYNAMODB_IMAGE)
        		.withExposedPorts(8000)
                .withReuse(true);

        REDIS_CONTAINER.start();
        DYNAMODB_CONTAINER.start();
    }


	// 동적 설정값 매핑
    @DynamicPropertySource
    public static void overrideProps(DynamicPropertyRegistry registry){

        // redis
        registry.add("spring.redis.host", REDIS_CONTAINER::getHost);
        registry.add("spring.redis.port", () -> "" + REDIS_CONTAINER.getMappedPort(6379));

        // dynamo
        final String endpoint = String.format("http://%s:%s", DYNAMODB_CONTAINER.getHost(),
                DYNAMODB_CONTAINER.getMappedPort(8000));
        registry.add("cloud.aws.dynamodb.endpoint", () -> endpoint);
    }
}

📌 DynamoDB Table 생성

DynamoDBTestContainers 라이브러리를 활용해서 테스팅할 때, Table을 만들고 사용해야합니다!

AWS SDK에서 제공하는 DynamoDBMapper를 활용해서 다음과 같이 테이블을 만들 수 있습니다.

class TextMemoStateApiTest extends ContainerBaseTest {

    @Autowired
    private DynamoDBMapper dynamoDBMapper;

    @Autowired
    private AmazonDynamoDB amazonDynamoDb;

    @BeforeEach
    void setUp() {

        // test container 기반, dynamoDB table 생성

        CreateTableRequest createTextMemoStateLatestTableRequest = dynamoDBMapper.generateCreateTableRequest(TextMemoStateLatest.class)
                .withProvisionedThroughput(new ProvisionedThroughput(1L, 1L));

        TableUtils.createTableIfNotExists(amazonDynamoDb, createTextMemoStateLatestTableRequest);
    }
    
    // ... 중략
profile
어디야 벽벽 / 블로그 이전 -> byuk.dev

0개의 댓글