在很多时候,程序猿们更关注代码本身,而不愿意把时间花费在环境搭建上,这也是Docker变得越来越受欢迎的原因之一。test-containers是Docker生态圈中的一颗新星,其 主要针对测试领域、背靠Docker实现环境百宝箱功能

  test-containers: 你要的环境,我都有~

  假设我们现在需要一个redis-cluster环境来学习reids pipeline相关的代码知识,那么就需要搭建一套redis集群:

环境支持方式直接搭建docker搭建使用test-container
工作量(简单预估)100个单位30个单位15个单位

test-container环境支持(示例)

提示一 test-container基于Docker,使用test-container前需要安装Docker环境。

提示二 test-container提供的环境不能应用于生产、只能用于测试环境等场景。

使用test-container提供单机redis

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.GenericContainer;
    import org.testcontainers.junit.jupiter.Container;
    import org.testcontainers.junit.jupiter.Testcontainers;
    import org.testcontainers.utility.DockerImageName;
    
    /**
     * 单机redis环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:14:18
     */
    @Slf4j
    @Testcontainers
    @Import(value = {ExcludedAllAutoConfiguration.class, RedisAutoConfiguration.class})
    @ContextConfiguration(initializers = RedisStandaloneEnvSupport .Initializer.class)
    public class RedisStandaloneEnvSupport implements RedisEnvSupport {
        
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "redis:5.0.3-alpine";
        
        /** docker开启的端口 */
        private static final int CONTAINER_PORT = 6379;
        
        @Container
        public static GenericContainer<?> redisContainer = new GenericContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME)).withExposedPorts(CONTAINER_PORT);
        
        /**
         * init application context
         *
         * @author {@link JustryDeng}
         * @since 2020/11/14 19:22:23
         */
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            @Override
            public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
                String redisIpAddress = redisContainer.getContainerIpAddress();
                log.info("redisIpAddress is [{}]", redisIpAddress);
                System.setProperty("spring.redis.host", redisIpAddress);
                Integer redisPort = redisContainer.getMappedPort(CONTAINER_PORT);
                log.info("redisPort is [{}]", redisPort);
                Assert.assertNotNull("redisPort is null", redisPort);
                System.setProperty("spring.redis.port", redisPort.toString());
            }
        }
        
    }
    
  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.redis.RedisStandaloneEnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    import javax.annotation.Resource;
    import java.time.Duration;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:36:21
     */
    @Slf4j
    @SpringBootTest
    public class RedisStandaloneTest extends RedisStandaloneEnvSupport {
        
        @Resource
        StringRedisTemplate stringRedisTemplate;
        
        @Test
        void one() throws InterruptedException {
            String name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertNull(name);
            
            String justryDeng = "JustryDeng";
            stringRedisTemplate.opsForValue().set("name", justryDeng, Duration.ofSeconds(3));
            name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertEquals(justryDeng, name);
            
            TimeUnit.SECONDS.sleep(3);
            name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertNull(name);
        }
    
    }
    
  • 输出
    在这里插入图片描述

使用test-container提供集群redis

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.redis.helper.ClientResources4Test;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.FixedHostPortGenericContainer;
    import org.testcontainers.containers.GenericContainer;
    import org.testcontainers.junit.jupiter.Container;
    import org.testcontainers.junit.jupiter.Testcontainers;
    
    /**
     * redis集群环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:14:18
     */
    @Slf4j
    @Testcontainers
    @Import(value = {ExcludedAllAutoConfiguration.class, ClientResources4Test.class, RedisAutoConfiguration.class})
    @ContextConfiguration(initializers = RedisClusterEnvSupport.Initializer.class)
    public class RedisClusterEnvSupport implements RedisEnvSupport {
        
        
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "grokzen/redis-cluster:6.0.7";
        
        /**
         * 集群最好用FixedHostPortGenericContainer, 主动避免端口冲突即可
         */
        @Container
        @SuppressWarnings("deprecation")
        public static GenericContainer<?> redisContainer = new FixedHostPortGenericContainer<>(DOCKER_IMAGE_NAME)
                .withFixedExposedPort(7000, 7000)
                .withFixedExposedPort(7001, 7001)
                .withFixedExposedPort(7002, 7002)
                .withFixedExposedPort(7003, 7003)
                .withFixedExposedPort(7004, 7004)
                .withFixedExposedPort(7005, 7005);
        
        /**
         * init application context
         *
         * @author {@link JustryDeng}
         * @since 2020/11/14 19:22:23
         */
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            @Override
            public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
                String redisIpAddress = redisContainer.getContainerIpAddress();
                String nodesInfo = redisIpAddress + ":" + 7000
                        + "," + redisIpAddress + ":" + 7001
                        + "," + redisIpAddress + ":" + 7002
                        + "," + redisIpAddress + ":" + 7003
                        + "," + redisIpAddress + ":" + 7004
                        + "," + redisIpAddress + ":" + 7005;
                log.info("spring.redis.cluster.nodes is [{}]", nodesInfo);
                System.setProperty("spring.redis.cluster.nodes", nodesInfo);
            }
        }
    }
    
  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.redis.RedisClusterEnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    import javax.annotation.Resource;
    import java.time.Duration;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:36:21
     */
    @Slf4j
    @SpringBootTest
    public class RedisClusterTest extends RedisClusterEnvSupport {
        
        @Resource
        StringRedisTemplate stringRedisTemplate;
        
        @Test
        void one() throws InterruptedException {
            String name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertNull(name);
            
            String justryDeng = "JustryDeng";
            stringRedisTemplate.opsForValue().set("name", justryDeng, Duration.ofSeconds(3));
            name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertEquals(justryDeng, name);
            
            TimeUnit.SECONDS.sleep(3);
            name = stringRedisTemplate.opsForValue().get("name");
            System.err.println(name);
            Assert.assertNull(name);
        }
    
    }
    
  • 输出
    在这里插入图片描述

使用test-container提供MySQL

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import com.zaxxer.hikari.HikariDataSource;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.ClassRule;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.MySQLContainer;
    import org.testcontainers.utility.DockerImageName;
    
    /**
     * Mysql8环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/17 14:14:43
     */
    @Slf4j
    @ContextConfiguration(initializers = Mysql8EnvSupport.Initializer.class)
    @Import(value = {ExcludedAllAutoConfiguration.class, DataSourceAutoConfiguration.class})
    public class Mysql8EnvSupport implements MysqlEnvSupport{
        
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "mysql:8.0.22";
    
    /** 数据库 */
    private static final String DATABASE = "mine_database";
    
    /** 连接池类型 */
    private static final Class<?> POOL_TYPE_CLASS = HikariDataSource.class;
    
    @ClassRule
    public static MySQLContainer<?> mySqlContainer = new MySQLContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME))
            .withDatabaseName(DATABASE)
            // 初始化脚本
            .withInitScript("mysql/init_mysql.sql")
            /// 配置文件
            ///.withConfigurationOverride("mysql/config")
            ;
    
    /**
     * init application context
     *
     * @author {@link JustryDeng}
     * @since 2020/11/14 19:22:23
     */
    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
            // start
            mySqlContainer.start();
            
            String jdbcUrl = mySqlContainer.getJdbcUrl();
            int endIndex = jdbcUrl.indexOf("?");
            String additionalSetting = "?characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false";
            if (endIndex < 0) {
                jdbcUrl = jdbcUrl + additionalSetting;
            } else {
                jdbcUrl = jdbcUrl.substring(0, endIndex) + additionalSetting;
            }
            System.setProperty("spring.datasource.url", jdbcUrl);
            System.setProperty("spring.datasource.username", mySqlContainer.getUsername());
            System.setProperty("spring.datasource.password", mySqlContainer.getPassword());
            System.setProperty("spring.datasource.driver-class-name", mySqlContainer.getDriverClassName());
            System.setProperty("spring.datasource.type", POOL_TYPE_CLASS.getName());
        }
    }
    
    }
    
  • 基类中涉及到的脚本的位置及内容为
    在这里插入图片描述

  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.mysql.Mysql8EnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:36:21
     */
    @Slf4j
    @SpringBootTest
    public class Mysql8Test extends Mysql8EnvSupport {
        
        @Resource
        TestMapper testMapper;
        
        @Test
        void one() {
            System.err.println(testMapper.count());
            List<Map<String, Object>> x = testMapper.selectAll();
            System.err.println(x);
        }
        
        @Mapper
        public interface TestMapper {
            
            @Select("select count(*) from employee")
            int count();
            
            @Select("select * from employee")
            List<Map<String, Object>> selectAll();
        }
        
    }
    
  • 输出
    在这里插入图片描述

使用test-container提供pgSQL

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import com.zaxxer.hikari.HikariDataSource;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.ClassRule;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.PostgreSQLContainer;
    import org.testcontainers.utility.DockerImageName;
    
    /**
     * PostgreSql10环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/17 21:14:29
     */
    @Slf4j
    @ContextConfiguration(initializers = PostgreSql10EnvSupport.Initializer.class)
    @Import(value = {ExcludedAllAutoConfiguration.class, DataSourceAutoConfiguration.class})
    public class PostgreSql10EnvSupport implements PostgreSqlEnvSupport {
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "postgres:10.15";
        
        /** 数据库 */
        private static final String DATABASE = "mine_database";
        
        /** 连接池类型 */
        private static final Class<?> POOL_TYPE_CLASS = HikariDataSource.class;
        
        @ClassRule
        public static PostgreSQLContainer<?> pgSqlContainer = new PostgreSQLContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME))
                .withDatabaseName(DATABASE)
                // 初始化脚本
                .withInitScript("postgresql/init_postgresql.sql");
        
        /**
         * init application context
         *
         * @author {@link JustryDeng}
         * @since 2020/11/14 19:22:23
         */
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            @Override
            public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
                // start
                pgSqlContainer.start();
                
                System.setProperty("spring.datasource.url", pgSqlContainer.getJdbcUrl());
                System.setProperty("spring.datasource.username", pgSqlContainer.getUsername());
                System.setProperty("spring.datasource.password", pgSqlContainer.getPassword());
                System.setProperty("spring.datasource.driver-class-name", pgSqlContainer.getDriverClassName());
                System.setProperty("spring.datasource.type", POOL_TYPE_CLASS.getName());
            }
        }
        
    }
    
  • 基类中涉及到的脚本的位置及内容为
    在这里插入图片描述

  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.postgresql.PostgreSql10EnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/13 16:36:21
     */
    @Slf4j
    @SpringBootTest
    public class PostgreSql10Test extends PostgreSql10EnvSupport {
        
        @Resource
        TestMapper testMapper;
        
        @Test
        void one() {
            System.err.println(testMapper.count());
            System.err.println(testMapper.selectAll());
        }
        
        @Mapper
        public interface TestMapper {
            
            @Select("select count(*) from employee")
            int count();
            
            @Select("select * from employee")
            List<Map<String, Object>> selectAll();
        }
        
    }
    
  • 输出
    在这里插入图片描述

使用test-container提供Kafka

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.ClassRule;
    import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.kafka.annotation.EnableKafka;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.KafkaContainer;
    import org.testcontainers.utility.DockerImageName;
    
    /**
     * 一个简单的kafka环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/18 12:11:54
     */
    @Slf4j
    @EnableKafka
    @ContextConfiguration(initializers = KafkaSimpleEnvSupport.Initializer.class)
    @Import(value = {ExcludedAllAutoConfiguration.class, KafkaAutoConfiguration.class})
    public class KafkaSimpleEnvSupport implements KafkaEnvSupport {
        
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "confluentinc/cp-kafka:5.4.3";
        
        @ClassRule
        public static KafkaContainer kafkaContainer = new KafkaContainer(DockerImageName.parse(DOCKER_IMAGE_NAME));
        
        /**
         * init application context
         *
         * @author {@link JustryDeng}
         * @since 2020/11/14 19:22:23
         */
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            
            @Override
            public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
                // start
                kafkaContainer.start();
                
                String bootstrapServers = kafkaContainer.getBootstrapServers();
                System.setProperty("spring.kafka.bootstrap-servers", bootstrapServers);
                // 因为是测试环境, 这里用earliest, 以避免在没有提交offset情况下,用latest不会读取旧数据的问题
                System.setProperty("spring.kafka.consumer.auto-offset-reset", "earliest");
            }
        }
    }
    
  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.kafka.KafkaSimpleEnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.annotation.Import;
    import org.springframework.kafka.annotation.KafkaListener;
    import org.springframework.kafka.core.KafkaTemplate;
    
    import javax.annotation.Resource;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/18 12:15:47
     */
    @Slf4j
    @SpringBootTest
    @Import(value = {KafkaSimpleTest.MyConsumer.class, KafkaSimpleTest.MyProducer.class})
    public class KafkaSimpleTest extends KafkaSimpleEnvSupport {
        
        static final String TOPIC_NAME4TEST = "my-topic4test";
        
        @Resource
        MyProducer myProducer;
        
        @Test
        void one() throws InterruptedException {
            // 生产消息
            for (int i = 0; i < 10; i++) {
                myProducer.sendMessage("邓沙利文-" + i);
            }
            
            // sleep几秒,以便观察消费者是否消费消息
            TimeUnit.SECONDS.sleep(3);
            Assert.assertTrue("消费消息失败(或消费消息超时)", MyConsumer.hasConsumedMsg);
        }
        
        /**
         * 生产者
         */
        @Slf4j
        public static class MyProducer {
            
            @Resource
            private KafkaTemplate<String, String> kafkaTemplate;
            
            public void sendMessage(String message) {
                kafkaTemplate.send(TOPIC_NAME4TEST, "key-" + UUID.randomUUID().toString(), message);
            }
        }
        
        /**
         * 消费者
         */
        @SuppressWarnings("unused")
        @Slf4j
        public static class MyConsumer {
            
            /** 是否已经消费了消息 */
            static boolean hasConsumedMsg = false;
            
            @KafkaListener(topics = TOPIC_NAME4TEST, groupId = "group-b")
            public void consumerOne(String message) {
                log.info("consumerOne消费了消息 [{}]", message);
                hasConsumedMsg = true;
            }
        }
    }
    
  • 输出
    在这里插入图片描述

使用test-container提供RabbitMQ

  • 环境基类

    import com.niantou.testcontainer.ExcludedAllAutoConfiguration;
    import com.niantou.testcontainer.author.JustryDeng;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.ClassRule;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    import org.springframework.test.context.ContextConfiguration;
    import org.testcontainers.containers.RabbitMQContainer;
    
    /**
     * 一个简单的RabbitMQ环境支持
     *
     * @author {@link JustryDeng}
     * @since 2020/11/20 12:49:45
     */
    @Slf4j
    @ContextConfiguration(initializers = RabbitMqSimpleEnvSupport.Initializer.class)
    @Import(value = {ExcludedAllAutoConfiguration.class})
    public class RabbitMqSimpleEnvSupport {
        
        /** 标准的docker镜像(即${镜像名}:${tag名}) */
        private static final String DOCKER_IMAGE_NAME = "rabbitmq:management-alpine";
        
        @ClassRule
        public static RabbitMQContainer rabbitMqContainer = new RabbitMQContainer(DOCKER_IMAGE_NAME)
                .withAdminPassword("ds123");
                
        
        /**
         * init application context
         *
         * @author {@link JustryDeng}
         * @since 2020/11/14 19:22:23
         */
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
            
            @Override
            public void initialize(@SuppressWarnings("NullableProblems") ConfigurableApplicationContext configurableApplicationContext) {
                // start
                rabbitMqContainer.start();
                
                String host = rabbitMqContainer.getHost();
                log.info("spring.rabbitmq.host={}", host);
                System.setProperty("spring.rabbitmq.host", host);
                
                Integer amqpPort = rabbitMqContainer.getAmqpPort();
                log.info("spring.rabbitmq.port={}", amqpPort);
                System.setProperty("spring.rabbitmq.port", String.valueOf(amqpPort));
        
                String adminUsername = rabbitMqContainer.getAdminUsername();
                log.info("spring.rabbitmq.username={}", adminUsername);
                System.setProperty("spring.rabbitmq.username", adminUsername);
        
                String adminPassword = rabbitMqContainer.getAdminPassword();
                log.info("spring.rabbitmq.password={}", adminPassword);
                System.setProperty("spring.rabbitmq.password", adminPassword);
            }
        }
        
    }
    
  • 测试类

    import com.niantou.testcontainer.author.JustryDeng;
    import com.niantou.testcontainer.rabbitmq.RabbitMqSimpleEnvSupport;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.jupiter.api.Test;
    import org.springframework.amqp.core.AmqpTemplate;
    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.DirectExchange;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Import;
    
    import javax.annotation.Resource;
    import java.util.concurrent.TimeUnit;
    
    import static com.niantou.testcontainer.rabbitmq.test.RabbitMqSimpleTest.MyConfig.MY_EXCHANGE_NAME;
    import static com.niantou.testcontainer.rabbitmq.test.RabbitMqSimpleTest.MyConfig.QUEUE_NAME;
    import static com.niantou.testcontainer.rabbitmq.test.RabbitMqSimpleTest.MyConfig.ROUTING_KEY;
    
    /**
     * 测试
     *
     * @author {@link JustryDeng}
     * @since 2020/11/18 12:15:47
     */
    @Slf4j
    @SpringBootTest
    @Import(value = {RabbitMqSimpleTest.MyConsumer.class, RabbitMqSimpleTest.MyProducer.class,
            RabbitMqSimpleTest.MyConfig.class})
    public class RabbitMqSimpleTest extends RabbitMqSimpleEnvSupport {
        
        @Resource
        MyProducer myProducer;
        
        @Test
        void one() throws InterruptedException {
            // 生产消息
            for (int i = 0; i < 10; i++) {
                myProducer.sendMessage("邓沙利文-" + i);
            }
            
            // sleep几秒,以便观察消费者是否消费消息
            TimeUnit.SECONDS.sleep(3);
            Assert.assertTrue("消费消息失败(或消费消息超时)", MyConsumer.hasConsumedMsg);
        }
        
        /**
         * 配置类
         */
        @Slf4j
        public static class MyConfig {
            
            static final String ROUTING_KEY = "myRoutingKey";
            
            static final String QUEUE_NAME = "my-queue";
            
            static final String MY_EXCHANGE_NAME = "my-direct-exchange";
            
            @Bean
            public Queue myQueue() {
                return new Queue(QUEUE_NAME);
            }
            
            @Bean
            public DirectExchange myDirectExchange() {
                return new DirectExchange(MY_EXCHANGE_NAME);
            }
            
            @Bean
            public Binding myBinding(Queue myQueue, DirectExchange myDirectExchange) {
                return BindingBuilder.bind(myQueue).to(myDirectExchange).with(ROUTING_KEY);
            }
            
        }
        
        /**
         * 生产者
         */
        @Slf4j
        public static class MyProducer {
            
            @Resource
            private AmqpTemplate amqpTemplate;
            
            public void sendMessage(String message) {
                amqpTemplate.convertAndSend(MY_EXCHANGE_NAME, ROUTING_KEY, message);
            }
        }
        
        /**
         * 消费者
         */
        @Slf4j
        public static class MyConsumer {
            
            /** 是否已经消费了消息 */
            static boolean hasConsumedMsg = false;
            
            @SuppressWarnings("unused")
            @RabbitListener(queues = QUEUE_NAME)
            public void consumerOne(String message) {
                log.info("consumerOne消费了消息 [{}]", message);
                hasConsumedMsg = true;
            }
        }
    }
    
  • 输出
    在这里插入图片描述


test-containers,初步学习完毕!

^_^ 如有不当之处,欢迎指正

^_^ 参考链接
         https://www.testcontainers.org/

^_^ 测试代码托管链接
         https://github.com/JustryDeng…test-containers

^_^ 本文已经被收录进《程序员成长笔记》 ,笔者JustryDeng

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐