为何Spock的setupSpec无法获取Spring Boot的@LocalServerPort?
问题原因及解决方案
核心差异原因
问题本质是Spock的类级生命周期与Spring的实例注入时机不匹配:
- Spock中被
@Shared修饰的字段属于类级共享变量,会在测试类的任何实例创建前完成初始化;setupSpec方法也是在所有测试方法执行前仅执行一次,且执行时测试类的实例还未被Spring容器初始化。 - Spring的
@LocalServerPort注解是针对测试类的实例字段进行注入的,只有当Spring容器完成测试实例的初始化后,才会把随机端口值注入到对应的实例字段中。
因此给localPort加上@Shared时,setupSpec执行时Spring还未完成端口注入,此时localPort为null;去掉@Shared后,localPort是实例字段,setup方法在每个测试实例初始化后执行,Spring已经完成端口注入,所以能拿到正确值。
优化解决方案(实现setupSpec中一次性初始化HttpClient)
如果想在setupSpec中只初始化一次HttpClient,可以通过Spring的Environment或ApplicationContext获取随机端口,避免依赖实例字段的注入:
@SpringBootTest(webEnvironment = RANDOM_PORT) class BeerControllerOpenApiTest extends Specification { @Shared HttpClient beerClient @Shared @Autowired Environment environment void setupSpec() { // 从Environment中获取随机端口 def localPort = environment.getProperty("local.server.port", Integer) beerClient = new HttpClient(baseUrl: "http://localhost:$localPort") } def "test list beers"() { when: def response = beerClient.get( path: "/api/v1/beer", headers: ["Content-Type": "application/json"] ) then: response.statusCode == 200 } }
也可以直接注入ApplicationContext来获取端口:
@SpringBootTest(webEnvironment = RANDOM_PORT) class BeerControllerOpenApiTest extends Specification { @Shared HttpClient beerClient @Shared @Autowired ApplicationContext applicationContext void setupSpec() { def localPort = applicationContext.environment.getProperty("local.server.port", Integer) beerClient = new HttpClient(baseUrl: "http://localhost:$localPort") } // 测试方法... }
内容的提问来源于stack exchange,提问作者spartanhooah
相关产品推荐
相关产品推荐

