ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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);
    }

     

    성공 !

     

    댓글

Designed by Tistory.