Embedded Redis for Spring Boot
Asked Answered
V

5

39

I run my Integration Test cases with Spring Boot with the help of my local Redis server on my machine.

But I want an embedded Redis server which is not dependent on any server and can run on any environment, like the H2 in-memory database. How can I do it?

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@IntegrationTest("server.port:0")
@SpringApplicationConfiguration(classes = Application.class) 
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MasterIntegrationTest {

}
Vagal answered 11/9, 2015 at 13:3 Comment(2)
did you check github.com/kstyrc/embedded-redisFoodstuff
Yes i checked but i am a little bit confused i mean i just want to know do i have to create @Bean RedisServer getserver(){} to inject it ?Vagal
S
50

You can use an embedded Redis like https://github.com/kstyrc/embedded-redis

  1. Add the dependency to your pom.xml
  2. Adjust the properties for your integration test to point to your embedded redis, for example :

    spring:
      redis:
        host: localhost
        port: 6379
    
  3. Instanciate the embedded redis server in a component that is defined in your tests only :

    @Component
    public class EmbededRedis {
    
        @Value("${spring.redis.port}")
        private int redisPort;
    
        private RedisServer redisServer;
    
        @PostConstruct
        public void startRedis() throws IOException {
            redisServer = new RedisServer(redisPort);
            redisServer.start();
        }
    
        @PreDestroy
        public void stopRedis() {
            redisServer.stop();
        }
    }
    
Spectroheliograph answered 12/1, 2016 at 7:59 Comment(12)
is there any way to set password for the embedded server ?Piquet
probably, check out the doc of the github project, it seems to be pretty configurableAmadeus
The project doesn't seem to be actively maintained anymore. Last release was close to two years ago.Durtschi
github.com/ozimov/embedded-redis is the successor of github.com/kstyrc/embedded-redis, I added an extra answer using ozimov/embedded-redis.Glowing
I'm using the successor - and I know this is an old post, but looks like the lack of logging with this make it hard to use in a CICD context - atm tests work on local windows machine - but when committed to build machine they fail - and no way to write logs. Any idea? its a unix build machine.Photochromy
maybe something about the ports being already in use on the build machine ?Amadeus
Not working with Redisson client, github.com/kstyrc/embedded-redis/issues/94Zilvia
you can my answer here: #50456921Zilvia
How to stop the embedded redis server? I had not added stop method, it was just startRequite
I have seen in a number of places, that embedded Redis is only recommended for testing. Why is this?Tremulous
Found Netflix version of embedded-redis, with newer redis support: bintray.com/spinnaker/spinnaker/embedded-redisShaveling
@Piquet Regarding the password, I was able to successfully set it using .setting("requirepass " + redisPassword). This was with the ozimov versionMoravian
G
28

edit : I would now strongly suggest to go the testcontainers route as explained in @magiccrafter answer, I am also using it for my tests now.


You can use ozimov/embedded-redis as a Maven(-test)-dependency (this is the successor of kstyrc/embedded-redis).

  1. Add the dependency to your pom.xml

     <dependencies>
       ...
       <dependency>
         <groupId>it.ozimov</groupId>
         <artifactId>embedded-redis</artifactId>
         <version>0.7.1</version>
         <scope>test</scope>
       </dependency>
    
  2. Adjust your application properties for your integration test

     spring.redis.host=localhost
     spring.redis.port=6379
    
  3. Use the embedded redis server in a test configuration

     @TestConfiguration
     public static class EmbededRedisTestConfiguration {
    
       private final redis.embedded.RedisServer redisServer;
    
       public EmbededRedisTestConfiguration(@Value("${spring.redis.port}") final int redisPort) throws IOException {
         this.redisServer = new redis.embedded.RedisServer(redisPort);
       }
    
       @PostConstruct
       public void startRedis() {
         this.redisServer.start();
       }
    
       @PreDestroy
       public void stopRedis() {
         this.redisServer.stop();
       }
     }
    
Glowing answered 8/11, 2017 at 15:49 Comment(3)
This also is not maintained!! (Valorievalorization
It is only wise to move away from embedded-redis as it is not maintained. Another response suggesting to use testcontainers should now be the accepted solution https://mcmap.net/q/399966/-embedded-redis-for-spring-bootSerafinaserafine
codemonstur/embedded-redis, ponfee/embedded-redis, simbahebinbo/embedded-redis and signalapp/embedded-redis are somewhat recently updated forks of embedded-redis. There are some differences between them, e.g. only the first two support Windows.Cyclamen
M
13

Another neat way is to use the testcontainers library which can run any type of application that can in a Docker container and Redis is no exception. What I like best is that it is lightly coupled with the Spring Test ecosystem.

maven's dependency:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>${testcontainers.version}</version>
</dependency>

simple integration test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"management.port=0"})
@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
@DirtiesContext
public abstract class AbstractIntegrationTest {

    private static int REDIS_PORT = 6379;

    @ClassRule
    public static GenericContainer redis = new GenericContainer("redis:5-alpine").withExposedPorts(REDIS_PORT);

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext ctx) {
            // Spring Boot 1.5.x
            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(ctx,
                "spring.redis.host=" + redis.getContainerIpAddress(),
                "spring.redis.port=" + redis.getMappedPort(REDIS_PORT));

            // Spring Boot 2.x.
            TestPropertyValues.of(
                "spring.redis.host:" + redis.getContainerIpAddress(),
                "spring.redis.port:" + redis.getMappedPort(REDIS_PORT))
                .applyTo(ctx);
        }
    }
}

Since Spring Framework 5.2.5 (Spring Boot 2.3.x) you can use the powerful DynamicPropertySource annotation. Here is an example:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public abstract class AbstractIT {

    static GenericContainer redisContainer = new GenericContainer("redis:5-alpine").withExposedPorts(6379);

    @DynamicPropertySource
    static void properties(DynamicPropertyRegistry r) throws IOException {
        r.add("spring.redis.host", redisContainer::getContainerIpAddress);
        r.add("spring.redis.port", redisContainer::getFirstMappedPort);
    }
}
Marcheshvan answered 1/6, 2018 at 14:38 Comment(1)
Is there way to know redis container image name based on the test container library we are using?Yearling
A
1

you can see this repository: https://github.com/caryyu/spring-embedded-redis-server , fully integrated with Spring and Spring Boot

maven dependency

<dependency>
<groupId>com.github.caryyu</groupId>
<artifactId>spring-embedded-redis-server</artifactId>
<version>1.1</version>
</dependency>

spring boot annotation

@Bean
public RedisServerConfiguration redisServerConfiguration() {
return new RedisServerConfiguration();
}

usage of application.yml

spring:
    redis:
        port: 6379
        embedded: true
Amnion answered 7/3, 2018 at 3:16 Comment(2)
Repository documentation is in Chinese, if you find an English version please post the link.Simard
this project is basically a wrapper of the one mentioned in the accepted answer. PointlessIntransigent
T
1

If your are using spring and reactive to access data with redis reactively. Meaning you're having a ReactiveRedisConnectionFactory (with a RedisConnectionFactory bean) and a LettuceConnectionFactory then you may want to follow this approach to set an embedded redis for multiple test classes.

First add the playtika embedded redis to your dependencies:

dependencies {
    testCompile("com.playtika.testcontainers:embedded-redis:2.0.9")
}

Then set the redis host and port as the embedded.redis one in your application.yml (that are generated by the embedded redis as env variable on creation).

spring:
  redis:
    host: \${embedded.redis.host:localhost}
    port: \${embedded.redis.port:6739}

In a bootstrap-redisnoauth.properties file, set the env variable embedded.redis.requirepass=false so that it does not require password.

Then in your test use the active profile:

@ActiveProfiles("redisnoauth")

And make sure to have this @TestConfiguration in your test class as well so that will connect you to the redis spawned on a randomly generated port.

@Category(IntegrationTest.class)
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("redisnoauth")
public class RedisCacheTest {

    @TestConfiguration
    static class RedisTestConfiguration {
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory(@Value("${spring.redis.host}") String host,
                                                             @Value("${spring.redis.port}") int port) {
            return new LettuceConnectionFactory(host, port);
        }
    
        @Bean
        public RedisOperations<String, String> stringKeyAndStringValueRedisOperations(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            redisTemplate.setKeySerializer(new StringRedisSerializer(UTF_8));
            redisTemplate.setValueSerializer(new StringRedisSerializer(UTF_8));
            return redisTemplate;
        }
    }

    @Test
    public void myTest() {
      // your test
    }

}

And it should work smoothly.

Ticktock answered 13/9, 2021 at 13:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.