我对Kotlin/Mockito问题感到非常沮丧
我想要完成的非常简单,我在我的springboot应用程序上有一个AuthorizationFilter,为了测试目的,我想模拟它的行为,让测试请求通过。
我的AuthorizationFilter确实调用了一个API,该API将提供用户auth状态。所以我想,最简单的模拟方法就是在过滤器中模拟AuthApi,然后返回我想要的状态.但它是随机工作的
@Component
class AuthorizationFilter(
val authApi: authApi
) : OncePerRequestFilter() {
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
if (request.method.equals("OPTIONS")) {
filterChain.doFilter(request, response)
return
}
val token = request.getHeader("authorization")
if (token == null) {
response.sendError(401)
return
}
runCatching {
authApi.authorize(token.replace("Bearer ", ""))
}.onSuccess {
AuthorizationContext.set(it)
filterChain.doFilter(request, response)
}.onFailure {
it.printStackTrace()
response.sendError(401)
}
}
}
authApi授权方法与这个问题无关,但是让我们知道它永远不会返回null.它可能会引发异常,但不会返回null
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class SocketIOServerTest {
@MockBean
lateinit var mockedApiComponent: AuthApi
@Autowired
lateinit var boardRepository: BoardRepository
@Autowired
private lateinit var servletRegistrationBean: ServletRegistrationBean<SocketIOServer>
private lateinit var socketIOServer: SocketIOServer
@LocalServerPort
private val serverPort: String? = null
lateinit var clientSocket: Socket
private val userId = 1
private val groupId = 123
private val admin = false
private val auth = Authorization("token", userId, groupId, admin)
private val objectMapper = ObjectMapper()
@BeforeAll
fun connect() {
AuthorizationContext.set(auth)
Mockito.`when`(mockedApiComponent.authorize(anyOrNull())).thenReturn(auth)
socketIOServer = servletRegistrationBean.servlet
clientSocket = IO.socket("http://localhost:${serverPort}", IO.Options.builder().setExtraHeaders(mutableMapOf(Pair("Authorization", listOf("Bearer token")))).build())
clientSocket.on(Socket.EVENT_CONNECT) {
println("client connected")
}
clientSocket.on(Socket.EVENT_DISCONNECT) {
println("client disconnected")
}
clientSocket.connect()
}
@Test
fun testPingPong() {
var finished = false
clientSocket.on("pong") {
println("event: ${it[0]}")
val pongTime = (it[0] as String).substring(18, 31).toLong()
assertTrue(System.currentTimeMillis() - pongTime < 1000)
finished = true
}
clientSocket.emit("ping")
while (!finished) Thread.yield()
}
@Test
fun testBasicNotification(){
clientSocket.on("basic_notification"){
println(Arrays.toString(it))
}
socketIOServer.send(SocketIOEvent("${groupId}","basic_notification","data"))
Thread.sleep(1000)
}
@Test
fun testBoardNotification() {
clientSocket.on("entity_create") {
val event = it[0] as String
println("event: $event")
val eventValue = objectMapper.readValue(event, Map::class.java)
val entityValue = eventValue["entity"] as Map<*, *>
assertEquals("BOARD", eventValue["entity_type"])
assertEquals("board name", entityValue["name"])
assertEquals(groupId, entityValue["groupId"])
assertEquals(userId, entityValue["created_by"])
assertEquals(userId, entityValue["last_modified_by"])
}
val board = boardRepository.save(Board(groupId, "board name"))
//boardRepository.delete(board)
}}
要明确的是,测试是有效的,断言是正确的,虽然它在结尾有一些随机行为,但它起作用.但是由于一些疯狂的行为,它打印了一个很大的堆栈跟踪
如您所见,我使用的是一个SocketIO客户端,它从代码中发送几个请求.其中一些通过身份验证,还有一些在这一行中抛出nullpointerexception。
.onSuccess {
AuthorizationContext.set(it) //this line
filterChain.doFilter(request, response)
}.
因为it
是空的,因为mockedApiComponent.authorize()
以某种方式返回空.同样,这在实际组件上是不可能的,而且不应该发生,因为模拟清楚地声明要返回哪个对象。
我彻底地删除了这段代码,认为junit得到了AuthApi
的两个bean,但是整个执行显示了与模拟匹配的相同的对象id .更奇怪的是,用于授权的token
参数总是相同的。
有谁可以帮我?
发布于 2022-07-03 07:30:00
我已经彻底地删除了这段代码,我认为junit得到了AuthApi的两个bean,但是整个执行显示了与模拟匹配的相同的对象id .更奇怪的是,授权时使用的令牌参数总是相同的。
这在我看来是令人不安的,就像运行时异步代码的一些问题。我会尝试做几件事:
AuthorizationContext.set(it)
,并放置更多调试代码以了解发生了什么。或者只是从那里调试,recover{}
块来处理NullPointerException,从那里调试查看堆栈跟踪runCatching{}
时会发生什么?https://stackoverflow.com/questions/72708712
复制相似问题