测试WebSocket控制器时,@WebMvcTest与@ContextConfiguration的比较

我目前正在为一个使用 "WebSockets "的Spring Boot项目中的 "controllers "编写测试。由于这方面的信息很难找到,我唯一的线索就是文档中推荐的这个例子。请允许我尝试解释一下我到目前为止的探索,试图理解并建立我的测试环境。我遵循基于上下文的方法,我在@WebMvc测试和@ContextConfiguration(该例子使用)之间纠结。我使用@WebMvcTest的动机是Spring Boot文档中的这一行。

When testing Spring Boot applications, this [using @ContextConfiguration(classes=…​) in order to specify which Spring @Configuration to load, or using nested @Configuration classes within your test] is often not required. Spring Boot’s @*Test annotations search for your primary configuration automatically whenever you do not explicitly define one.

因此,@WebMvcTest似乎特别适合这项任务,因为它只关注Web层,将扫描的Bean集合限制在必要的范围内(例如@controller),而不是启动一个完整的ApplicationContext

我下面的代码例子使用字段注入来初始化通道拦截器,以捕获通过它们发送的消息。

@Autowired private AbstractSubscribableChannel clientInboundChannel;
@Autowired private AbstractSubscribableChannel clientOutboundChannel;
@Autowired private AbstractSubscribableChannel brokerChannel;

据我所知,这些字段使得例子中的TestConfig类(完整的类定义见最后的代码块)的存在是必要的,因为没有它,我得到一个错误,说 “no beans qualify as autowire candidates”"。我相信TestConfig中的这两个字段是关键所在。

@Autowired
private List<SubscribableChannel> channels;
@Autowired
private List<MessageHandler> handlers;

然而,如果没有@ContextConfiguration(classes = [WebSocketConfig::class])(WebSocketConfig是我自己的WebSocket配置文件),这两个字段总是为空,导致错误。到目前为止,这意味着需要@ContextConfiguration(classes = [WebSocketConfig::class])与TestConfig的存在相结合。

有趣的是,如果没有@WebMvcTest,clientInboundChannel、clientOutboundChannel和brokerChannel实际上从未被初始化。因此,我需要@WebMvcTest@ContextConfiguration,这似乎有点奇怪。而且最近一次更新的例子库已经有两年多了,我无法摆脱这种感觉,它可能有点过时了。

这是我的测试类(Kotlin)目前的样子。为了简洁起见,我省略了createRoom的测试案例。

@WebMvcTest(controllers = [RoomController::class])
@ContextConfiguration(classes = [WebSocketConfig::class, RoomControllerTests.TestConfig::class])
class RoomControllerTests {
    @Autowired
    private lateinit var clientInboundChannel: AbstractSubscribableChannel

    @Autowired
    private lateinit var clientOutboundChannel: AbstractSubscribableChannel

    @Autowired
    private lateinit var brokerChannel: AbstractSubscribableChannel

    private lateinit var clientOutboundChannelInterceptor: TestChannelInterceptor

    private lateinit var brokerChannelInterceptor: TestChannelInterceptor

    private lateinit var sessionId: String

    @BeforeEach
    fun setUp() {
        brokerChannelInterceptor = TestChannelInterceptor()
        clientOutboundChannelInterceptor = TestChannelInterceptor()
        brokerChannel.addInterceptor(brokerChannelInterceptor)
        clientOutboundChannel.addInterceptor(clientOutboundChannelInterceptor)
    }

    @Test
    fun createRoom() {
        // test room creation
        // ...
    }

    @Configuration
    internal class TestConfig : ApplicationListener<ContextRefreshedEvent?> {
        @Autowired
        private val channels: List<SubscribableChannel>? = null

        @Autowired
        private val handlers: List<MessageHandler>? = null

        override fun onApplicationEvent(event: ContextRefreshedEvent) {
            for (handler in handlers!!) {
                if (handler is SimpAnnotationMethodMessageHandler) {
                    continue
                }
                for (channel in channels!!) {
                    channel.unsubscribe(handler)
                }
            }
        }
    }
}

StackOverflow: spring - @WebMvcTest vs @ContextConfiguration when testing WebSocket controllers - Stack Overflow