組み込みRedisを使ってSpringBootTest

# 概要

- Redisを使用しているSpringBootアプリケーションのテストを実装していたのだが、テストは「完全にlocalで実行できるべし」が信条なのでサーバーに構築したRedisにはアクセスしたくない…
- そんなときに組み込みRedisが使えそうだということを知ったので、まとめてみました

# 先にまとめ

- `it.ozimov:embedded-redis`というライブラリを使う
- `RedisServer#start`により組み込みRedisを起動する

# 使用ライブラリ

ライブラリ  | バージョン
--- | ---
org.springframework.boot:spring-boot-starter-data-redis | 2.3.5.RELEASE
org.springframework.boot:spring-boot-starter-test | 2.3.5.RELEASE 
it.ozimov:embedded-redis | 0.7.2

# サンプルアプリの実装

まずは依存関係周り
`it.ozimov:embedded-redis`が組み込みRedisを利用するために必要なライブラリです

```groovy:build.gradle
plugins {
    id 'org.springframework.boot' version '2.3.5.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

sourceCompatibility = 11
targetCompatibility = 11

test {
    useJUnitPlatform()
}

repositories {
    mavenCentral()
    maven {
        url "http://maven.springframework.org/milestone"
    }
    maven { url 'https://repo.spring.io/milestone' }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'it.ozimov:embedded-redis:0.7.2'
}
```

次にサンプルのAPI
/setを叩いてから/getを叩くことで`value`という文字列が返却されるはず

```java:SampleController.java
@RequiredArgsConstructor
@RestController
public class SampleController {
    private final RedisTemplate<String, String> redisTemplate;

    @GetMapping("/set")
    public void set() {
        redisTemplate.opsForValue().set("key", "value");
    }

    @GetMapping("/get")
    public String get() {
        return redisTemplate.opsForValue().get("key");
    }
}
```

次に設定周り ※デフォルトの設定を明示的に記載しています

```yaml:application.yml
spring:
  redis:
    host: localhost
    port: 6379
```

# テストの実装

組み込みRedisを使うために、Test用のConfigurationクラスを用意してあげます

```java:RedisTestAutoConfiguration.java
@TestConfiguration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisTestAutoConfiguration {
private static RedisServer redisServer = null;

    /**
     * constructor
     */
    public RedisAutoConfiguration(RedisProperties redisProperties) {
        // サーバーを起動
        start(redisProperties);
    }

    public static synchronized void start(RedisProperties redisProperties) {
        if (redisServer == null) {
            redisServer = new RedisServer(redisProperties.getPort());
            redisServer.start();
        }
    }

    @PreDestroy
    public void preDestroy() {
        // サーバを停止
        stop();
    }

    public static synchronized void stop() {
        if (redisServer != null) {
            redisServer.stop();
            redisServer = null;
        }
    }
}
```

`RedisProperties`はspringbootが提供してくれている、redis関連のConfigurationPropertyクラスです。
ポートを指定して`RedisServer`を生成後、`start`メソッドで起動できます。

最後にテストクラスです！

```java
@ExtendWith(SpringExtension.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = RedisTestAutoConfiguration.class)
class ApplicationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void test() {
        restTemplate.getForObject("/set", Void.class);
        var value = restTemplate.getForObject("/get", String.class);

        assertThat(value).isEqualTo("value");
    }
}
```

`@SpringBootTest`で先ほど実装した`RedisTestAutoConfiguration`を指定することで組み込みのRedisサーバーを利用することができます！

※もちろんこの指定がないと、localにRedisを立てない限りテストが通りません😇

# 参考

- https://www.baeldung.com/spring-embedded-redis