SpringBoot使用WebSocket后启动单元测试报错

在SpringBoot项目中,增加WebSocket功能之后(SpringBoot使用WebSocket实现前后端消息推送)执行单元测试,发现会报错。

java.lang.IllegalStateException: Failed to load ApplicationContext

查看异常栈信息

Caused by: org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'serverEndpointExporter' defined in class path resource [com/lqc/app/config/WebSocketConfig.class]: 
Invocation of init method failed; nested exception is java.lang.IllegalStateException: 
javax.websocket.server.ServerContainer not available

大意是,在创建WebSocket的ServerEndpointExporter这个bean的时候创建失败,原因是找不到javax.websocket.server.ServerContainer这个接口的实现类,但是我们正常启动项目都能启动起来,为啥会没有这个类呢?

查看@SpringBootTest注解的内容,我们发现默认的WebEnvironment是MOCK类型,而MOCK的embedded值为false。也就是说,默认的单元测试是不真正启动web容器(嵌入的tomcat),而ServerEndpointExporter这个bean依赖的javax.websocket.server.ServerContainer这个接口的实现类org.apache.tomcat.websocket.server.WsServerContainer恰好就在web容器中。

因此默认的MOCK类型由于不启动web容器继而导致ServerEndpointExporter创建失败进而导致单元测试报错。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
public @interface SpringBootTest {
    @AliasFor("properties")
    String[] value() default {};
    @AliasFor("value")
    String[] properties() default {};
    String[] args() default {};
    Class<?>[] classes() default {};
    SpringBootTest.WebEnvironment webEnvironment() default SpringBootTest.WebEnvironment.MOCK;
    public static enum WebEnvironment {
        MOCK(false),
        RANDOM_PORT(true),
        DEFINED_PORT(true),
        NONE(false);
        private final boolean embedded;
        private WebEnvironment(boolean embedded) {
            this.embedded = embedded;
        }
        public boolean isEmbedded() {
            return this.embedded;
        }
    }
}

问题原因找到了,解决问题的办法也很简单,我们手动指定一下WebEnvironment即可。在@SpringBootTest上增加一个参数,代码如下:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

单元测试即可正常运行。