실제 서버와 격리된 테스트 Redis 인스턴스를 사용함으로써, 테스트 간 간섭 없이 안정적인 테스트가 가능
기본 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 테스트 코드 작성
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'
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();
}
}
성공적으로 테스트 완료 !
