【发布时间】:2021-06-24 18:41:02
【问题描述】:
我目前正在使用 WebSockets 的 Spring Boot 项目中为我的控制器编写测试。
由于很难获得有关该主题的信息,我唯一的线索是docs 推荐的this example。
请允许我尝试解释我到目前为止的尝试,尝试理解并设置我的测试环境。我遵循基于上下文的方法,我在@WebMvcTest 和@ContextConfiguration(示例使用)之间纠结。
我使用 @WebMvcTest 的动机完全是 Spring Boot docs 中的这一行:
在测试 Spring Boot 应用程序时,通常不需要 [使用
@ContextConfiguration(classes=…)来指定要加载哪个 Spring@Configuration,或在测试中使用嵌套的@Configuration类]。只要您没有明确定义,Spring Boot 的@*Test注释就会自动搜索您的主要配置。
@WebMvcTest 因此似乎特别适合这项任务,因为它只关注 web 层 limiting the set of scanned beans 只关注那些必要的(例如@controller),而不是旋转完整的ApplicationContext。
我下面的代码示例使用字段注入来初始化通道拦截器以捕获通过它们发送的消息。
@Autowired private AbstractSubscribableChannel clientInboundChannel;
@Autowired private AbstractSubscribableChannel clientOutboundChannel;
@Autowired private AbstractSubscribableChannel brokerChannel;
据我所知,这些字段使得示例中必须存在 TestConfig 类(请参阅末尾的代码块以获取完整的类定义),因为没有它,我会收到一个错误说不bean 有资格成为 autowire 候选人。我相信TestConfig中的这两个字段是关键:
@Autowired
private List<SubscribableChannel> channels;
@Autowired
private List<MessageHandler> handlers;
但是,如果没有@ContextConfiguration(classes = [WebSocketConfig::class]) (WebSocketConfig 是我自己的 WebSocket 配置文件),这两个字段总是null 导致错误。
到目前为止,这意味着需要 @ContextConfiguration(classes = [WebSocketConfig::class]) 和 TestConfig 的存在。
有趣的是,如果没有 @WebMvcTest、clientInboundChannel、clientOutboundChannel 和 brokerChannel,则永远不会真正初始化。所以这留给我的是我需要@WebMvcTest 和 @ContextConfiguration,这在某种程度上看起来很奇怪。
由于示例 repo 的最后一次更新已有两年多的历史,我无法摆脱它可能有些过时的感觉。
这就是我的测试类(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)
}
}
}
}
}
【问题讨论】:
标签: spring spring-boot controller integration-testing spring-websocket