我目前正在为一个使用 "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