Home 테스트 병렬 실행
Post
Cancel

테스트 병렬 실행

테스트컨테이너(TestContainer)의 병렬 실행에 대해 작성한 글입니다. 퍼가실 땐 출처를 밝혀주세요. 학습 과정에서 작성된 글이기 때문에 잘못된 내용이 있을 수 있으며, 이에 대한 지적이나 피드백은 언제든 환영입니다.

image









1. 테스트컨테이너의 문제점


테스트컨테이너의 가장 큰 문제점은 실행 속도입니다. 아래는 약 15개 정도의 통합/문서화 테스트를 포함한 빌드한 시간인데, 1분 26초 정도가 나온 것을 볼 수 있습니다. 만약 테스트가 많아진다면 빌드 속도가 가파르게 증가합니다.

image









이는 내부적으로 도커 컨테이너를 띄우기 때문인데, 도커 컨테이너를 띄우고, 컨테이너가 뜰 때까지 기다리는 작업이 포함돼 있어 상당히 느립니다. 만약 테스트가 순차적으로 실행될 경우, 한 테스트가 끝날 때까지 다음 테스트는 실행되지 못하므로 전체 빌드 시간이 증가하게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Data
public class GenericContainer<SELF extends GenericContainer<SELF>>
    extends FailureDetectingExternalResource
    implements Container<SELF>, AutoCloseable, WaitStrategyTarget, Startable {


    ......

    protected void doStart() {
        try {
            
            ......

            Unreliables.retryUntilSuccess(
                startupAttempts,
                () -> {
                    ......
                    // 컨테이너 구동
                    tryStart(startedAt);
                    return true;
                }
            );
        } catch (Exception e) {
            throw new ContainerLaunchException("Container startup failed", e);
        }
    }

    ......

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Data
public class GenericContainer<SELF extends GenericContainer<SELF>>
    extends FailureDetectingExternalResource
    implements Container<SELF>, AutoCloseable, WaitStrategyTarget, Startable {


    ......

    private void tryStart(Instant startedAt) {
        try {
            
            // 컨테이너 생성 및 설정
            CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName);

            ......

            // 컨테이너 구동
            dockerClient.startContainerCmd(containerId).exec();

            ......

            try {
                // 컨테이너가 생성되지 않았다면 기다림
                waitUntilContainerStarted();
            } catch (Exception e) {
                ......
            throw new ContainerLaunchException("Could not create/start container", e);
        }
    }

    ......

}









2. 해결


이 문제를 테스트 병렬 실행으로 개선할 수 있습니다.

image









단, 단순히 병렬로 테스트를 실행하면 안 되는데, 아무런 조건 없이 이를 실행할 때 아래와같이 무수히 많은 테스트 컨테이너가 띄워지기 때문입니다.

image









테스트컨테이너는 컨테이너가 띄워지기 전 내부적으로 컨테이너가 띄워져 있는지를 먼저 체크합니다. 따라서 이를 동기화해서 여러 개의 테스트 컨테이너가 띄워지지 않도록 해줘야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Data
public class GenericContainer<SELF extends GenericContainer<SELF>>
    extends FailureDetectingExternalResource
    implements Container<SELF>, AutoCloseable, WaitStrategyTarget, Startable {

    ......

    private void tryStart(Instant startedAt) {
        try {
            String dockerImageName = getDockerImageName();
            logger().debug("Starting container: {}", dockerImageName);

            logger().info("Creating container for image: {}", dockerImageName);
            CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName);
            applyConfiguration(createCommand);

            createCommand.getLabels().putAll(DockerClientFactory.DEFAULT_LABELS);

            boolean reused = false;
            final boolean reusable;

            // 변수 체크
            if (shouldBeReused) {
                if (!canBeReused()) {
                    throw new IllegalStateException("This container does not support reuse");
                }
            }

            ......

        }
    }
}









아래와 같이요. synchronized 키워드를 사용하면 이런 문제를 해결하고 테스트를 안전하게 병렬로 실행할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@IntegrationTest
public abstract class IntegrationTestBase {

    ......

    @BeforeAll
    static void beforeAll() {
       synchronized (TestContainer.class) {
            TestContainer.start();
       }
    }

    ......

}









이를 통해 성능이 1/4 단축된 것을 볼 수 있습니다. 이는 테스트가 많아질수록 가파르게 빨라집니다.

image

image









3. 정리


테스트컨테이너는 많은 장점을 가지고 있지만, 느린 구동 시간 때문에 사용하기 부담스럽다는 단점이 있습니다. 하지만 테스트를 병렬로 실행하면 이런 성능 문제를 해결할 수 있으므로 적극 활용해 볼 것을 권장해 드립니다.


This post is licensed under CC BY 4.0 by the author.

멀티 모듈에서 RestDocs 문서가 흩어질 때, 어떻게 해결할까?

CPU Cache