테스트를 위한 Embedded Redis

yshjft·2022년 7월 23일
0

CI/CD를 공부하고 해당 방법을 사용해보며 한가지 느끼게된 점이 있습니다. 지금까지 테스트 코드를 작성하면 로컬에서만 사용하였기 때문에 필요한 외부 의존성(ex. DB)을 직접 실행시켜 사용하였습니다. 하지만 CI 툴에서는 해당 방법이 불가능하기에 다른 방법을 생각해봐야했습니다. DB의 경우 Embedded h2를 사용하면 된다는 것을 알았지만 Redis는 어떻게 해야하는지 몰랐고 방법을 찾아보았습니다. 그 결과 아래와 같은 글들을 보고 기존 프로젝트에 Embedded Redis를 적용해보았습니다.

[Redis] SpringBoot Data Redis 로컬/통합 테스트 환경 구축하기

내장 Redis 설정기

실습은 이전 클론 코딩 프로젝트인 coretime을 기준으로 작성하였습니다.


의존성 추가

build.gradle에 Embedded Redis를 위한 의존성을 추가합니다.

testImplementation 'it.ozimov:embedded-redis:0.7.2'

spring-data-radis는 이미 의존성이 추가 되어 있기에 따로 명시하지 않았습니다.


EmbeddedRedisConfig

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import redis.embedded.RedisServer;

@Configuration
public class EmbeddedRedisConfig {
  @Value("${spring.redis.port}")
  private int port;
  private RedisServer redisServer;

  @PostConstruct
  public void redisServer() throws IOException {
    System.out.println("시발 이게 왜 저기서 되는데");
    redisServer = new RedisServer(isRedisRunning()? findAvailablePort() : port);
    redisServer.start();
  }

  @PreDestroy
  public void stopRedis() {
    if (redisServer != null) {
      redisServer.stop();
    }
  }

  /**
   * Embedded Redis가 현재 실행중인지 확인
   */
  private boolean isRedisRunning() throws IOException {
    return isRunning(executeGrepProcessCommand(port));
  }

  /**
   * 현재 PC/서버에서 사용가능한 포트 조회
   */
  public int findAvailablePort() throws IOException {

    for (int port = 10000; port <= 65535; port++) {
      Process process = executeGrepProcessCommand(port);
      if (!isRunning(process)) {
        return port;
      }
    }

    throw new IllegalArgumentException("Not Found Available port: 10000 ~ 65535");
  }

  /**
   * 해당 port를 사용중인 프로세스 확인하는 sh 실행
   */
  private Process executeGrepProcessCommand(int port) throws IOException {
    String command = String.format("netstat -nat | grep LISTEN|grep %d", port);
    String[] shell = {"/bin/sh", "-c", command};
    return Runtime.getRuntime().exec(shell);
  }

  /**
   * 해당 Process가 현재 실행중인지 확인
   */
  private boolean isRunning(Process process) {
    String line;
    StringBuilder pidInfo = new StringBuilder();

    try (BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))) {

      while ((line = input.readLine()) != null) {
        pidInfo.append(line);
      }

    } catch (Exception e) {
    }

    return StringUtils.hasText(pidInfo.toString());
  }
}

위는 Embedded Redis를 테스트에 적용하는 코드입니다. (여러 스프링 테스트 컨텍스트가 실행되면 Embedded Redis에서 포트충돌이 발생하기 때문에 사용 가능한 포트를 확인하고 설정하는 코드가 포함되어 있습니다.)

하면서 신기했던 점이 있었는데 redis.embedded.RedisServertest디렉토리에서만 사용이 가능하다는 것이었습니다.


나머지

Embedded Redis에 연결하는 코드의 경우 실제 Redis에 연결하는 기존 코드를 그대로 사용하면 됩니다.

import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Getter
@Setter
public class RedisConfig {

  private final Logger log = LoggerFactory.getLogger(getClass());

  private String host;

  private int port;

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    log.info("host: {}, port: {}", host, port);
    return new LettuceConnectionFactory(host, port);
  }

  @Bean
  public RedisTemplate<String, String> redisTemplate() {
    RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new StringRedisSerializer());
    redisTemplate.setConnectionFactory(redisConnectionFactory());
    return redisTemplate;
  }
}
profile
꾸준히 나아가자 🐢

0개의 댓글