【问题标题】:Spring Boot Integration test random free portSpring Boot 集成测试随机空闲端口
【发布时间】:2016-08-16 14:30:55
【问题描述】:

我能够获得 Spring Boot 集成以生成一个随机的空闲端口来启动它自己。但我还需要一个用于 Redis 的免费端口。

@ContextConfiguration(classes = {MyApplication.class}, loader = SpringApplicationContextLoader.class)
@WebIntegrationTest(randomPort = true, value = "server.port:0")
@ActiveProfiles(profiles = {"local"})
public class SegmentSteps {

    private static final String HOST_TEMPLATE = "http://localhost:%s";

    // Needs to be a random open port
    private static final int REDIS_PORT = 6380;

    private String host;
    @Value("${local.server.port}")
    private int serverPort;

    private RedisServer redisServer;

    @Before
    public void beforeScenario() throws Exception {
        host = String.format(HOST_TEMPLATE, serverPort);
        redisServer = RedisServer.builder()
                .redisExecProvider(RedisExecProvider.defaultProvider())
                .port(REDIS_PORT)
                .setting("bind 127.0.0.1")
                .build();
        redisServer.start();
    }

    ...
}

关于如何实现这一点的任何想法?

【问题讨论】:

    标签: java spring spring-boot integration-testing spring-test


    【解决方案1】:

    您可以使用 Spring Framework 的 SocketUtils 获取可用端口:

    int redisPort = SocketUtils.findAvailableTcpPort();
    

    【讨论】:

    • 有没有办法在上下文中获得相同的数字?
    • 还是我只使用一个 int bean?有限定词?
    • @ptimson,我不明白你的问题。你能详细说明一下吗?
    【解决方案2】:

    在 2021 年,这是我认为的最佳方式。这需要 spring-boot >= 2.3 / spring framework 5.2.5 并且它使用@DynamicPropertySource 注解。

    助手类:

    @TestConfiguration
    public class TestRedisConfiguration
    {
        private RedisServer redisServer;
    
        public TestRedisConfiguration(
                @Value("${spring.redis.port}")
                        int redisPort
        )
        {
            this.redisServer = new RedisServer(redisPort);
        }
    
        @PostConstruct
        public void postConstruct()
        {
            redisServer.start();
        }
    
        @PreDestroy
        public void preDestroy()
        {
            redisServer.stop();
        }
    
        static public void configurePort(DynamicPropertyRegistry r)
        {
            int port = SocketUtils.findAvailableTcpPort();
            r.add("spring.redis.host", () -> "localhost");
            r.add("spring.redis.port", () -> port);
        }
    }
    

    在您的测试类(或基类)中:

    @SpringBootTest
    @Import({TestRedisConfiguration.class})
    public abstract class BaseTestFullContext
    {
    ...
       @DynamicPropertySource
        static public void properties(DynamicPropertyRegistry r)
        {
            TestRedisConfiguration.configurePort(r);
        }
    ...
    

    【讨论】:

      【解决方案3】:

      您还可以使用您选择的 Java 客户端或利用 Overcast 在 Docker 中运行您的 Redis。如果使用 Overcast,通过激活 exposeAllPorts 选项,您的 Redis 将绑定到主机上的随机端口。

      至于如何在上下文中启用该属性 - 这需要一些工作,但您可以实现一个侦听器,该侦听器将启动 Docker 容器并将端口作为属性放入环境中:

      public class IntegrationTestBootstrapApplicationListener implements
          ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
      
          public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 4;
          public static final int PROPERTY_SOURCE_NAME = "integrationTestProps";
      
          private int order = DEFAULT_ORDER;
      
          public void setOrder(int order) {
              this.order = order;
          }
      
          @Override
          public int getOrder() {
              return this.order;
          }
      
          @Override
          public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
              ConfigurableEnvironment environment = event.getEnvironment();
      
              if (!environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
                  CloudHost itestHost = CloudHostFactory.getCloudHost("redis");
                  itestHost.setup();
      
                  String host = itestHost.getHostName();
                  // fetch the dynamic port from Docker
                  int port = itestHost.getPort(6379); 
      
                  // alternatively, skip the whole CloudHost setup above and just use:
                  // int port = SocketUtils.findAvailableTcpPort();
      
                  environment.getPropertySources().addLast(
                    new MapPropertySource(
                      PROPERTY_SOURCE_NAME, Collections.<String, Object> singletonMap(
                        "redis.port", port));
                  );
              }
          }
      
      }
      

      【讨论】:

      • 伟大的将不得不试一试 - 这个项目可能有点矫枉过正
      • 是的,想通了,但如果您需要配置多个外部资源(例如,Redis、RabbitMQ、PostgreSQL 和 FTP 服务器),这是一个很好的选择。
      猜你喜欢
      • 2020-02-20
      • 1970-01-01
      • 2019-07-11
      • 2020-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-20
      • 2020-04-24
      相关资源
      最近更新 更多