How to find port of Spring Boot container when running a spock test using property server.port=0
Asked Answered
T

3

8

Given this entry in application.properties:

server.port=0

which causes Spring Boot to chose a random available port, and testing a spring boot web application using spock, how can the spock code know which port to hit?

Normal injection like this:

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

doesn't work with spock.

Tremulous answered 9/7, 2014 at 0:9 Comment(0)
T
14

You can find the port using this code:

int port = context.embeddedServletContainer.port

Which for those interested in the java equivalent is:

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

Here's an abstract class that you can extends which wraps up this initialization of the spring boot application and determines the port:

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);
    }
}

Which you can use like this:

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;
    }
}
Tremulous answered 9/7, 2014 at 0:9 Comment(0)
B
8

The injection will work with Spock, as long as you've configured your spec class correctly and have spock-spring on the classpath. There's a limitation in Spock Spring which means it won't bootstrap your Boot application if you use @SpringApplicationConfiguration. You need to use @ContextConfiguration and configure it manually instead. See this answer for the details.

The second part of the problem is that you can't use a GString for the @Value. You could escape the $, but it's easier to use single quotes:

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

Putting this together, you get a spec that looks something like this:

@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'
    }
}

Also note the use of @IntegrationTest("server.port=0") to request a random port be used. It's a nice alternative to configuring it in application.properties.

Bornstein answered 9/7, 2014 at 8:48 Comment(3)
@SpringApplicationConfiguration seems to be working using Spock 1.0.Baccy
Andy's answer should've been marked as the real answer. His solution for getting port# with the @Value string is much more elegant.Washer
BTW, @andy-wilkinson, spock-spring now supports @SpringBootTest as a single annotation instead of having to combine @ContextConfiguration, @WebAppConfiguration, and @IntegrationTests. So all you have to do is: @SpringBootTest (webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)Washer
O
0

You could do this too:

@Autowired
private org.springframework.core.env.Environment springEnv;
...
springEnv.getProperty("server.port");
Okie answered 7/2, 2018 at 3:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.