提问者:小点点

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


我目前正在一个使用WebSocket的Spring Boot项目中为我的控制器编写测试。由于很难获得有关该主题的信息,我唯一的线索是文档推荐的这个示例。请允许我尝试解释到目前为止我的尝试,尝试理解并设置我的测试环境。我遵循的是基于上下文的方法,我在@WebMvcTest@ContextConfiguration(示例使用)之间左右为难。我使用@WebMvcTest的动机是Spring Boot文档中的这句话:

在测试Spring Boot应用程序时,通常不需要使用此[使用@ContextCon人和(类=…)来指定要加载的Spring@Con人和,或者在测试中使用嵌套的@配置类]。Spring Boot的@*测试注释会在您没有明确定义时自动搜索您的主要配置。

因此,@WebMvcTest似乎特别适合该任务,因为它只关注web层,只将扫描的bean集限制为那些必需的bean(例如@controller),而不是旋转一个完整的ApplicationContext

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

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

据我所知,这些字段使得示例中的< code>TestConfig类(完整的类定义见最后的代码块)的存在成为必要,因为如果没有它,我会得到一个错误,说没有beans符合autowire候选的条件。我相信< code>TestConfig中的这两个字段是关键:

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

但是,如果没有< code > @ context configuration(classes =[WebSocketConfig::class])(< code > WebSocketConfig 是我自己的WebSocket配置文件),这两个字段总是< code>null导致错误。到目前为止,这意味着需要< code > @ context configuration(classes =[WebSocketConfig::class])与< code>TestConfig的组合。

有趣的是,如果没有< code > @ WebMvcTest ,< code > clientInboundChannel 、< code > clientOutboundChannel 和< code>brokerChannel实际上永远不会初始化。因此,留给我的是,我既需要< code>@WebMvcTest又需要< code > @ context configuration ,这似乎有点奇怪。示例回购的最后一次更新已经超过两年了,我无法摆脱这种感觉,即它可能有些过时了。

这就是我的测试类(静态编程语言)当前的样子。为了简洁起见,我省略了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)
                }
            }
        }
    }
}

共1个答案

匿名用户

这里发布的答案是我开始了解如何使用@WebMvcTest@ContextConfiguration的第一个线索。看起来真正必要的行是@ContextConfiguration(classes = [WebSocketConfig::class]),因为没有它,就找不到必要的配置(在这种情况下是我自己的WebSocketConfig)。

虽然< code>@WebMvcTest(以及一般的测试切片)确实会自动搜索主要配置,但是由主应用程序类定义的配置是找到的配置,因为该类是用< code > @ spring boot application 注释的。

更重要的是,像@WebMvctest这样的测试切片完全排除了扫描中的@配置类,这意味着它们不会包含在测试切片加载的应用程序上下文中。支持这一点的事实是,如果@WebMvctest被替换为@SpringBoottest-即使没有@ContextConfiation(类=[WebSocketConfig::class])-这意味着WebSocketConfig已加载。

总而言之,在我看来,有三种方法可以将非主要配置包含在应用程序上下文中,以便在集成测试中检测到它:

  • 使用@SpringBoottest注释测试类(并且可选地@ContextConfigation以将加载的bean数量限制为仅需要的数量)
  • 使用测试切片(例如@WebMvctest)结合@ContextConfigation
  • 注释测试类
  • 用测试切片注释测试类(例如@WebMvctest)并在主类中定义配置(即用@SpringBootApplication注释的类)