-
Redis :: 테스트 환경 구축Study 2022. 11. 22. 22:21
1000만건의 상품 데이터가 있는 쇼핑몰 프로젝트를 진행중입니다.
문제 발생
Redis TestCode 환경 구축이 필요합니다. 많은 곳에서 Redis를 사용중이었고, 다수의 클래스를 테스트하기에 Embedded Redis는 적합하지 않았습니다.
🤩 해결 방안
- Redis Test Container
- Java 언어만으로 구축 가능한 오픈소스 라이브러리
- 컨테이너로 동작하기 때문에 어느 환경에서든 바로 실행이 가능
CODE
Set Test Container
public abstract class RedisTestContainer { static final String REDIS_IMAGE = "redis:6-alpine"; static final GenericContainer REDIS_CONTAINER; static { REDIS_CONTAINER = new GenericContainer<>(DockerImageName.parse(REDIS_IMAGE)) .withExposedPorts(6379) .withReuse(true); REDIS_CONTAINER.start(); } @DynamicPropertySource public static void overrideProps(DynamicPropertyRegistry registry){ registry.add("SPRING_REDIS_HOST", REDIS_CONTAINER::getHost); registry.add("SPRING_REDIS_PORT", () -> ""+REDIS_CONTAINER.getFirstMappedPort()); } }
- redis:6-alpine 이미지에 새 컨테이너를 생성합니다.
- withExposedPorts(6379) ▶ 컨테이너의 포트를 6379로 열어줍니다.
withReuse(true) ▶ 해당 컨테이너를 재사용 여부 - @DynamicPropertySource ▶ 동적으로 설정 값 매핑
- 추상클래스로 등록해 필요한 클래스에서 상속하며 사용할 예정입니다.
- + 동적 매핑 과정 간단하게 정리 !
(1) 도커 Redis 이미지 생성 ( + 설정 추가 )
(2) @DtnamicPropertySource을 통해 이미지 설정 값 application.properties에 매핑
(3) RedisConfig.class에서 매핑된 설정 값 적용
(4) TestCode에서 사용 가능 !
Use Test Container
- 간단한 테스트를 진행합니다.
- 테스트가 진행될 때에는 Docket Desktop이 실행된 상태여야 합니다.
@SpringBootTest public class RedisTest extends RedisTestContainer { @Autowired private RedisTemplate<String, String> redisTemplate; @Test @DisplayName("Basic_Redis_Test") public void Basic_Redis_Test() { ValueOperations<String, String> values = redisTemplate.opsForValue(); values.set("TE", "ST"); assertEquals(values.get("TE"), "ST"); }
- Redis TestContainer가 잘 동작되는 상황입니다.
- Ryuk Container은 TestContainer를 모니터링하고,
테스트 종료 시 컨테이너와 이미지를 정리하는 역할을 합니다.
- 혹시라도 ClosedChannelException이 발생한다면 차분히 @Test 어노테이션의 import를 확인해보시면 좋을 것 같습니다.
아마 Junit4로 잘못 임포트 했을 확률이 큽니다. 만약 해결이 안된다면 정말 유감입니다..
- 이렇게 마무리 하는 것도 조금 아쉬우니 Redis Pipeline 테스트까지 진행해보겠습니다.
@BeforeAll public void Set_String_PipeLine() { ArrayList<String> list = new ArrayList<>(); list.add("T"); list.add("E"); list.add("S"); list.add("T!"); RedisSerializer keySerializer = redisTemplate.getStringSerializer(); RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); redisTemplate.executePipelined((RedisCallback<Object>) connection -> { list.forEach(i -> { connection.stringCommands().set(keySerializer.serialize("Test::" + i), valueSerializer.serialize(i)); }); return null; }); } @Test @DisplayName("PipeLine_Test") public void PipeLine_Test() { ValueOperations<String, String> values = redisTemplate.opsForValue(); assertEquals(values.get(("Test::T")), "T"); assertEquals(values.get(("Test::E")), "E"); assertEquals(values.get(("Test::S")), "S"); assertEquals(values.get(("Test::T!")), "T!"); }
- 하는 김에 ZSet Pipeline 테스트코드도 추가해보겠습니다.
@BeforeAll public void Set_ZSet_PipeLine() { ArrayList<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); RedisSerializer keySerializer = redisTemplate.getStringSerializer(); RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); redisTemplate.executePipelined((RedisCallback<Object>) connection -> { list.forEach(i -> { connection.zSetCommands().zAdd(keySerializer.serialize("ZSet"), Math.random() * 10, valueSerializer.serialize(i)); }); return null; }); } @Test @DisplayName("PipeLine_ZSet_Test") public void PipeLine_ZSet_Test() { ZSetOperations<String, String> values = redisTemplate.opsForZSet(); assertEquals(values.size("ZSet"), 4); }
성공 ! 'Study' 카테고리의 다른 글
클라우드 환경에서 CI/CD 기술 선택 기준 (0) 2022.11.23 Elastic Beanstalk 정리 (0) 2022.11.22 Redis :: 통계 데이터 배치처리 (0) 2022.11.22 Redis :: 다수의 명령어 파이프 라인으로 처리하기 (0) 2022.11.22 Redis :: 랭킹보드 구현하기 (0) 2022.11.22 - Redis Test Container