当使用属性server.port=0运行Spock测试时,如何找到Spring Boot容器的端口?

8

假设在application.properties文件中有以下内容:

server.port=0

本文涉及的问题是如何在使用Spring Boot开发Web应用时选择一个随机可用端口,以及如何在Spock中进行测试。当Spring Boot选择一个随机可用端口时,我们如何让Spock代码知道要访问哪个端口呢?

像这样进行普通注入:

@Value("${local.server.port}")
int port;

不兼容spock。

3个回答

14

你可以使用这段代码找到端口:

int port = context.embeddedServletContainer.port

对于那些对Java等效性感兴趣的人来说,它是:

int port = ((TomcatEmbeddedServletContainer)((AnnotationConfigEmbeddedWebApplicationContext)context).getEmbeddedServletContainer()).getPort();

这是一个抽象类,你可以继承它来包装Spring Boot应用程序的初始化并确定端口:

abstract class SpringBootSpecification extends Specification {

    @Shared
    @AutoCleanup
    ConfigurableApplicationContext context

    int port = context.embeddedServletContainer.port

    void launch(Class clazz) {
        Future future = Executors.newSingleThreadExecutor().submit(
                new Callable() {
                    @Override
                    public ConfigurableApplicationContext call() throws Exception {
                        return (ConfigurableApplicationContext) SpringApplication.run(clazz)
                    }
                })
        context = future.get(20, TimeUnit.SECONDS);
    }
}

你可以像这样使用:

class MySpecification extends SpringBootSpecification {
    void setupSpec() {
        launch(MyLauncher.class)
    }

    String getBody(someParam) {
        ResponseEntity entity = new RestTemplate().getForEntity("http://localhost:${port}/somePath/${someParam}", String.class)
        return entity.body;
    }
}

8
注入将与Spock一起使用,只要您正确配置了规范类并在类路径上有spock-spring。 Spock Spring存在限制, 这意味着如果您使用@SpringApplicationConfiguration,它不会引导您的Boot应用程序。 您需要手动使用@ContextConfiguration进行配置。有关详细信息,请参见this answer
问题的第二部分是您不能为@Value使用GString。 您可以转义$,但使用单引号更容易:
@Value('${local.server.port}')
private int port;

将这些内容整合起来,你会得到一个类似于以下的规范:
@ContextConfiguration(loader = SpringApplicationContextLoader, classes = SampleSpockTestingApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port=0")
class SampleSpockTestingApplicationSpec extends Specification {

    @Value("\${local.server.port}")
    private int port;

    def "The index page has the expected body"() {
        when: "the index page is accessed"
        def response = new TestRestTemplate().getForEntity(
            "http://localhost:$port", String.class);
        then: "the response is OK and the body is welcome"
        response.statusCode == HttpStatus.OK
        response.body == 'welcome'
    }
}

请注意使用@IntegrationTest("server.port=0")请求使用随机端口。这是一个很好的替代方法,不需要在application.properties中进行配置。

@SpringApplicationConfiguration 似乎在使用 Spock 1.0 时有效。 - Gilad Bar Orion
安迪的答案应该被标记为真正的答案。他使用@Value字符串获取端口号的解决方案更加优雅。 - Jacomoman
顺便提一下,@andy-wilkinson,spock-spring现在支持@SpringBootTest作为单个注释,而不必组合@ContextConfiguration@WebAppConfiguration@IntegrationTests。所以你只需要这样做:@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) - Jacomoman

0
你也可以这样做:
@Autowired
private org.springframework.core.env.Environment springEnv;
...
springEnv.getProperty("server.port");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接