繁体   English   中英

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

[英]Spring: How to set system properties in integration test when @ComponentScan is used?

摘要:在我的应用程序类中添加@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}"

我已经阅读了在springapplicationbuilder设置运行时属性的内容

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

并在@ComponentScan javadoc中:

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

但是我不明白为什么添加@ComponentScan批注后行为会发生变化。

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

(我的目标是使用@SpringBootApplication ,它包含@ComponentScan ,但是直到我能使用它时我才能这样做。)

添加我的想法(从我可以从您的代码中收集的所有信息):

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

-否则,你只能说:

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

希望能帮助到你。

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

解决方案1:以-Dabc=xyz格式添加Test参数,这会将属性abc添加到系统属性中。

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

解决方案3:只需让spring-boot加载诸如classpath:bootstrap.yml的属性,就可以在其中指定任何属性。

注释@ComponentScan将启用基于当前包或ComponentScan#basePackages自动扫描。 这意味着将对SampleWebSocketsApplicationTests.ClientConfiguration进行扫描,因为它们具有相同的基本包samples.websocket.jetty

然而, SampleWebSocketsApplicationTests.ClientConfiguration不应该被解析SpringJUnit4ClassRunner因为我们需要分析它SampleWebSocketsApplicationTests#echoEndpoint手动。 它只能由echoEndpoint()创建的ApplicationContext进行解析。

此外, @SpringBootApplication等于将@Configuration@EnableAutoConfiguration@ComponentScan一起使用,因此注释掉@ComponentScan@SpringBootApplication将具有相同的效果。

我的建议是举类SampleWebSocketsApplicationTests成包samples.websocket.jettytest (来自不同samples.websocket.jetty ),并启用@ComponentScan@SpringBootApplicationSampleJettyWebSocketsApplication ,然后再试一次。 它应该工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM