37

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 {

}
informatik01
  • 16,038
  • 10
  • 74
  • 104
Gurinder
  • 943
  • 2
  • 9
  • 19

5 Answers5

49

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();
        }
    }
    
Sébastien Nussbaumer
  • 6,202
  • 5
  • 40
  • 58
  • 1
    is there any way to set password for the embedded server ? – Renjith Oct 05 '16 at 08:01
  • probably, check out the doc of the github project, it seems to be pretty configurable – Sébastien Nussbaumer Oct 05 '16 at 08:11
  • 7
    The project doesn't seem to be actively maintained anymore. Last release was close to two years ago. – Mads Hoel Jan 12 '17 at 13:26
  • 10
    https://github.com/ozimov/embedded-redis is the successor of https://github.com/kstyrc/embedded-redis, I added an extra answer using ozimov/embedded-redis. – Markus Schulte Nov 08 '17 at 15:50
  • 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. – aspiringCoder May 24 '18 at 11:03
  • maybe something about the ports being already in use on the build machine ? – Sébastien Nussbaumer May 25 '18 at 13:29
  • Not working with Redisson client, https://github.com/kstyrc/embedded-redis/issues/94 – Nitin Jul 04 '18 at 05:10
  • you can my answer here: https://stackoverflow.com/questions/50456421/reliable-libraries-out-there-for-spring-boot-redis-integration-tests/51282408#51282408 – Nitin Jul 11 '18 at 09:55
  • How to stop the embedded redis server? I had not added stop method, it was just start – Jeff Cook Nov 28 '18 at 10:38
  • I have seen in a number of places, that embedded Redis is only recommended for testing. Why is this? – lafual Apr 01 '19 at 20:18
  • Found Netflix version of embedded-redis, with newer redis support: https://bintray.com/spinnaker/spinnaker/embedded-redis – talabes Aug 02 '19 at 20:45
  • 2
    @Renjith Regarding the password, I was able to successfully set it using `.setting("requirepass " + redisPassword)`. This was with the ozimov version – KC Baltz Sep 04 '19 at 18:04
28

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();
      }
    }
    
JeanValjean
  • 17,172
  • 23
  • 113
  • 157
Markus Schulte
  • 4,171
  • 3
  • 47
  • 58
  • 5
    This also is not maintained!! ( – KitKarson Jan 01 '22 at 22:13
  • 2
    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://stackoverflow.com/a/50645865/12221963 – Sahil Jain Jan 24 '23 at 00:39
  • 2
    [codemonstur/embedded-redis](https://github.com/codemonstur/embedded-redis), [ponfee/embedded-redis](https://github.com/ponfee/embedded-redis), [simbahebinbo/embedded-redis](https://github.com/simbahebinbo/embedded-redis) and [signalapp/embedded-redis](https://github.com/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. – gebi Mar 17 '23 at 07:44
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);
    }
}
verglor
  • 616
  • 7
  • 21
magiccrafter
  • 5,175
  • 1
  • 56
  • 50
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
Cary Yu
  • 99
  • 2
  • 4
0

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.

Sylhare
  • 5,907
  • 8
  • 64
  • 80