Spring:如何在使用@ComponentScan时在集成测试中设置系统属性?

时间:2016-04-18 06:32:59

标签: java spring spring-boot

摘要:将@ComponentScan(或@SpringBootApplication)注释添加到我的应用程序类会更改SpringApplicationBuilder.properties()的行为并中断我的集成测试。

我正在使用Spring Boot示例的简化版本: spring-boot-sample-websocket-jetty
我删除了除“echo”示例所需的所有内容(我使用的是Spring Boot 1.3.3)。

我留下了以下SampleJettyWebSocketsApplication代码:

@Configuration
@EnableAutoConfiguration
//@ComponentScan    // --- If I uncomment this the test breaks ---
@EnableWebSocket

public class SampleJettyWebSocketsApplication
    implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
    }

    @Bean
    public EchoService echoService() {
        return new DefaultEchoService("Did you say \"%s\"?");
    }

    @Bean
    public WebSocketHandler echoWebSocketHandler() {
        return new EchoWebSocketHandler(echoService());
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
    }
}

以下测试类(直接来自Spring Boot示例的代码):

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SampleJettyWebSocketsApplication.class)
@WebIntegrationTest({"server.port=0"})
@DirtiesContext
public class SampleWebSocketsApplicationTests {

    private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);

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

    @Test
    public void echoEndpoint() throws Exception {
        logger.info("Running the echoEndpoint test. Port: " + port + ". Path: /echo/websocket");
        ConfigurableApplicationContext context = new SpringApplicationBuilder(
                ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
                        .properties("websocket.uri:ws://localhost:" + this.port
                                + "/echo/websocket")
                        .run("--spring.main.web_environment=false");
        long count = context.getBean(ClientConfiguration.class).latch.getCount();
        AtomicReference<String> messagePayloadReference = context
                .getBean(ClientConfiguration.class).messagePayload;
        context.close();
        assertThat(count).isEqualTo(0);
        assertThat(messagePayloadReference.get())
                .isEqualTo("Did you say \"Hello world!\"?");
    }

    @Configuration
    static class ClientConfiguration implements CommandLineRunner {

        @Value("${websocket.uri}")
        private String webSocketUri;

        private final CountDownLatch latch = new CountDownLatch(1);

        private final AtomicReference<String> messagePayload = new AtomicReference<String>();

        @Override
        public void run(String... args) throws Exception {
            logger.info("Waiting for response: latch=" + this.latch.getCount());
            if (this.latch.await(10, TimeUnit.SECONDS)) {
                logger.info("Got response: " + this.messagePayload.get());
            }
            else {
                logger.info("Response not received: latch=" + this.latch.getCount());
            }
        }

        @Bean
        public WebSocketConnectionManager wsConnectionManager() {

            logger.info("Setting up SimpleClientWebSocketHandler...");
            WebSocketConnectionManager manager = new WebSocketConnectionManager(client(),
                    handler(), this.webSocketUri);
            manager.setAutoStartup(true);

            return manager;
        }

        @Bean
        public StandardWebSocketClient client() {
            return new StandardWebSocketClient();
        }

        @Bean
        public SimpleClientWebSocketHandler handler() {
            logger.info("Creating new SimpleClientWebSocketHandler using SimpleGreetingService...");
            return new SimpleClientWebSocketHandler(greetingService(), this.latch,
                    this.messagePayload);
        }

        @Bean
        public GreetingService greetingService() {
            return new SimpleGreetingService();
        }
    }

}

如上所述运行应用程序和单元测试一切正常但是如果我取消注释应用程序类上的@ComponentScan注释,应用程序仍然运行正常但测试中断错误:

Could not resolve placeholder 'websocket.uri' in string value "${websocket.uri}"

我已在setting-the-run-time-properties-on-springapplicationbuilder读到:

  

您在SpringApplicationBuilder上配置的属性在应用程序的环境中可用,而不是作为系统属性。

@ComponentScan javadoc中:

  

如果未定义特定包,则将从声明此批注的类的包中进行扫描。

但我不明白为什么在添加@ComponentScan注释时行为会发生变化。

当应用程序类使用websocket.uri(或@ComponentScan)进行注释时,如何在测试中设置 System 属性@SpringBootApplication

(我的目标是使用@SpringBootApplication,其中包含@ComponentScan,但在我开始工作之前我无法做到。)

2 个答案:

答案 0 :(得分:0)

添加我的想法(从我可以从你的代码收集的任何东西):

- 尝试在应用程序属性中添加属性websocket.uri,或者如果您的项目包含src / test / resources / test.properties,然后将其添加到test.properties文件中。@ ComponentScan应该选择它。

-Else,你可以说:

public static void main(String[] args) {
        System.setProperty("websocket.uri","<your uri>");
        SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
    }

希望它有所帮助。

答案 1 :(得分:0)

有几种方法可以添加系统属性。

解决方案1: 以if($state.current.name != 'app.details'){ $state.go('app.details.overview'); } 的格式添加Test的参数,这会将属性-Dabc=xyz添加到系统属性。

解决方案2: 就像0楼一样。

解决方案3: 只需让abc加载属性,例如spring-boot,就可以在其中指定任何属性。

注释classpath:bootstrap.yml将启用基于当前包或@ComponentScan的自动扫描。这意味着ComponentScan#basePackages将被扫描,因为它们具有相同的基础包SampleWebSocketsApplicationTests.ClientConfiguration

但是,samples.websocket.jetty不应解析SampleWebSocketsApplicationTests.ClientConfiguration因为我们需要手动在SpringJUnit4ClassRunner中解析它。它只能由SampleWebSocketsApplicationTests#echoEndpoint中创建的ApplicationContext解析。

还有,echoEndpoint()等于同时使用@SpringBootApplication@Configuration以及@EnableAutoConfiguration,因此请注释@ComponentScan或{{1}会有同样的效果。

我的建议是将课程@ComponentScan移至包@SpringBootApplication(与SampleWebSocketsApplicationTests不同)并在samples.websocket.jettytest启用samples.websocket.jetty@ComponentScan并尝试再次。它应该工作。