让其他组件知道@WebIntegrationTest随机端口

Vin*_*t F 5 spring spring-annotations spring-batch spring-boot

我最近几天一直在玩Spring引导,我非常喜欢@WebIntegrationTest注释,它允许启动Tomcat并自动部署Spring启动应用程序.它提供了在随机端口上启动Tomcat的选项,如果您正在测试webapp,这非常容易.但我的情况略有不同.整个源代码可在此处获得:

https://github.com/vincent-fuchs/spring-projects

使用Spring Batch,我需要解析Excel文件,并将项目写入REST webService.当我事先知道编写者需要使用的URL时,它能很好地工作,并且我能够使用Spring Boot在几秒钟内测试整个流程,方法是加载一个"空心"@RestController,它接收请求并使它们可用对断言的测试.

(注意:我确信这对很多人来说都非常有用.如果你试图测试Spring Integration配置,它也可以工作)

这是我要测试的批处理,而不是webService.但是为了测试这批产品的输出,我需要这个"空心"的web服务.

     @RunWith(SpringJUnit4ClassRunner.class)
     @SpringApplicationConfiguration(classes = 
     {
      //the webservice client that the batch should use (planning for SOAP impl later)
      RestCustomerWsClient.class,
      //the "target" web server, with the 'hollow' endpoint listening
      TargetRESTSystem.class,
      //the application under test and its config
      TestSpecificConfiguration.class,
      CustomerBatchConfiguration.class}
                                )
    @WebIntegrationTest("server.port:8080")
    @IntegrationTest({"spring.batch.job.enabled=false"})
    public class CustomerBatchWithRestTest {

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


    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Autowired
    private DummyCustomerController endpoint ;

    @Autowired
    private ConfigurableApplicationContext  appCtx ;

    @Autowired
    private ConfigurableEnvironment env;


    @Before
    public void loadDynamicProperty() {

        EnvironmentTestUtils.addEnvironment(appCtx, "target.port:"+targetWebServerPort);

        assertThat(env.getProperty("target.port")).isEqualTo(String.valueOf(targetWebServerPort)).as("port for target server is not register correctly in properties");
    }




    @Test
    public void targetShouldReceiveExpectedCustomer() throws Exception {


        JobExecution jobExecution = jobLauncherTestUtils.launchJob();

        //assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.COMPLETED);
        assertThat(endpoint.getReceivedCustomers()).hasSize(1);

        Customer actualCustomer=endpoint.getReceivedCustomers().get(0);
        assertThat(actualCustomer.getId()).isEqualTo(1);
        assertThat(actualCustomer.getFirstName()).isEqualTo("Vincent");
        assertThat(actualCustomer.getLastName()).isEqualTo("FUCHS");
    }

}
Run Code Online (Sandbox Code Playgroud)

通过这些注释,TargetRESTSystem将部署在端口8080上的tomcat中.

在批处理方面,写入的组件配置如下,使用属性文件给出target.port = 8080:

public class RestCustomerWsClient implements CustomerWsClient {

    @Value("${target.host}")
    private String targetHost="undefined";

    @Value("${target.port}")
    private String targetPort="undefined";

    @Override
    public void sendCustomer(Customer customerFromExcel) {

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForEntity("http://"+targetHost+":"+targetPort+"/integrate", customerFromExcel, null);

        System.out.println("sent client integration request");
    }
}   
Run Code Online (Sandbox Code Playgroud)

就像我说的,这很好用,因为我自己定义了端口并使WebIntegrationTest注释与批处理侧的属性文件之间的值匹配.

但是,如果我想让我的测试更加健壮并使用随机端口,因为如果8080已被其他进程使用,我不希望它失败怎么办?我可以用它代替:

@WebIntegrationTest(randomPort=true)
Run Code Online (Sandbox Code Playgroud)

我尝试了各种各样的东西,但没有一个是因为加载事物的顺序:

  • @Before方法将动态值放在属性中,但我需要刷新上下文后,Spring不会允许我
  • 我也试过调整ApplicationContextInitializer/AbstractGenericContextLoader,但是当它们被调用时,Tomcat还没有启动,我还没有随机值.

所以基本上,我需要我的测试来启动Tomcat,可能在其中部署我的空心web服务,给我随机端口,以便我可以在上下文中添加它,然后只加载我的TestSpecificConfiguration/CustomerBatchConfiguration.

知道这是否可行?或者WebIntegrationTest注释是否真正针对测试webapps,我们在测试中自己执行http请求?

谢谢

文森特

=====编辑

在我提交的最新版本的代码中,我使用反射来设置已在Spring上下文中加载的组件上的动态端口. https://github.com/vincent-fuchs/spring-projects/commit/df7bf3a76fc6109fdacac21d7dd10b045ccd0458

它有效,但显然它不是最好的解决方案.因此,如果一个人有一个干净的方式这样做,那将是伟大的!