대기열 시스템 테스트 코드 작성

박태현·2025년 5월 19일

기타

목록 보기
9/12

대기열 및 참가열의 등록, 삭제, 존재 유무, 순위 반환, 대기열에서 참가열로 이동에 대한 테스트 코드 작성

외부 Redis 서버에 의존하지 않고, 테스트 코드 내에서 임베디드 Redis를 실행해 테스트를 수행

실제 서버와 격리된 테스트 Redis 인스턴스를 사용함으로써, 테스트 간 간섭 없이 안정적인 테스트가 가능

Mac에서 M1, M2 등을 사용하는 경우

기본 embedded Redis 라이브러리가 실행되지 않는 호환성 문제가 있으므로
이를 해결하기 위해 직접 redis-server 바이너리를 프로젝트에 포함시키고, 그 경로를 명시적으로 지정해야 함

public class TestRedisConfiguration {

    private final RedisServer redisServer;

    // 포트는 0~65535 범위
    // Mac에서는 embedded redis Redis 바이너리 호환성 문제 때문에 redis-server 바이너리 경로를 수동으로 지정해줘야 함
    public TestRedisConfiguration() throws IOException {
        this.redisServer = new RedisServer(new File("src/main/resources/binary/redis/redis-mac-arm"), 6386);
    }

    @PostConstruct
    public void start() throws IOException {
        this.redisServer.start();
    }
    @PreDestroy
    public void stop() throws IOException {
        this.redisServer.stop();
    }
}

테스트 환경에서 Redis를 사용하기 위해 의존성 추가

// redis 테스트 코드 작성
implementation 'it.ozimov:embedded-redis:0.7.2'
testImplementation 'com.github.codemonstur:embedded-redis:1.4.3'

// WebFlux에서 Mono/Flux의 emit, 완료 여부, 오류 등을 검증하는 도구
testImplementation 'io.projectreactor:reactor-test'

Redis 설정을 위한 application-test.properties 파일 생성

spring.profiles.active=test
spring.data.redis.host=127.0.0.1
spring.data.redis.port=60000

테스트 코드 작성


@ActiveProfiles("test")
@Import({TestRedisConfiguration.class})
@SpringBootTest
public class UserEnterTest {

    @Autowired
    private UserService userService;

    @Autowired
    private ReactiveRedisTemplate<String, String> reactiveRedisTemplate;

    // 실행 전 redis 데이터 초기화
    @BeforeEach
    public void beforeEach() {
        ReactiveRedisConnection reactiveConnection = reactiveRedisTemplate.getConnectionFactory().getReactiveConnection();
        reactiveConnection.serverCommands().flushAll().subscribe();
    }

    @Test
    void 대기열_사용자_등록_테스트() {

        StepVerifier.create(userService.registerUserToWaitQueue("1번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(1L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("2번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(2L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("3번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(3L)
                .verifyComplete();

        // 예외 발생 테스트
        StepVerifier.create(userService.registerUserToWaitQueue("1번", "reserve", Instant.now().getEpochSecond()))
                .expectError(ReserveException.class)
                .verify();
    }

    @Test
    void 대기열_사용자_조회_테스트() {

        StepVerifier.create(userService.registerUserToWaitQueue("1번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(1L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("2번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(2L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("3번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(3L)
                .verifyComplete();

        // 조회 테스트
        StepVerifier.create(userService.searchUserRanking("1번", "reserve", "wait"))
                .expectNext(1L)
                .verifyComplete();

        // 예외 발생 테스트
        StepVerifier.create(userService.searchUserRanking("조회예외발생테스트", "reserve", "wait"))
                .expectErrorMatches(throwable ->
                        throwable instanceof ReserveException &&
                                ((ReserveException) throwable).getErrorCode() == ErrorCode.USER_NOT_FOUND_IN_THE_QUEUE
                )
                .verify();
    }

    @Test
    void 대기열_사용자_삭제_테스트() {

        StepVerifier.create(userService.registerUserToWaitQueue("1번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(1L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("2번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(2L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("3번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(3L)
                .verifyComplete();

        StepVerifier.create(userService.cancelWaitUser("1번", "reserve", "wait"))
                .verifyComplete();

        StepVerifier.create(reactiveRedisTemplate.opsForZSet().size("reserve" + WAIT_QUEUE))
                .expectNext(2L).verifyComplete();
    }

    @Test
    void 참가열_이동_및_참가열_사용자_삭제_테스트() {
        String queueType = "reserve";

        // 대기열 등록
        StepVerifier.create(userService.registerUserToWaitQueue("1번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(1L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("2번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(2L)
                .verifyComplete();

        StepVerifier.create(userService.registerUserToWaitQueue("3번", "reserve", Instant.now().getEpochSecond()))
                .expectNext(3L)
                .verifyComplete();

        // 참가열로 2명 이동
        StepVerifier.create(userService.allowUser(queueType, 2L))
                .expectNext(2L)
                .verifyComplete();

        // 참가열에 1번, 2번 존재 확인
        StepVerifier.create(
                reactiveRedisTemplate.opsForZSet().rank(queueType + ALLOW_QUEUE, "1번")
        ).expectNextMatches(Objects::nonNull).verifyComplete();

        StepVerifier.create(
                reactiveRedisTemplate.opsForZSet().rank(queueType + ALLOW_QUEUE, "2번")
        ).expectNextMatches(Objects::nonNull).verifyComplete();

        // 대기열에 남은 인원 1명 확인 (3번)
        StepVerifier.create(reactiveRedisTemplate.opsForZSet().size(queueType + WAIT_QUEUE))
                .expectNext(1L).verifyComplete();

        // 참가열에서 1번 삭제
        StepVerifier.create(userService.cancelWaitUser("1번", "reserve", "allow"))
                .verifyComplete();

        // 참가열에 남은 인원 1명 확인
        StepVerifier.create(reactiveRedisTemplate.opsForZSet().size(queueType + ALLOW_QUEUE))
                .expectNext(1L).verifyComplete();
    }
}

성공적으로 테스트 완료 !

profile
꾸준하게

0개의 댓글