Scalatra asyncPost接口单元测试请求串扰问题求助
问题描述
我是Scala新手,最近把Scalatra的POST端点改成asyncPost,并用Future替代Await.result后,原本正常的单元测试开始间歇性失败,出现测试A拿到测试B的请求数据这类串扰问题。单独运行每个测试都没问题,推测是跨测试的请求上下文串了,不确定是业务代码还是测试配置的问题。已经试过给每个测试初始化Hystrix上下文,但问题依旧。测试框架用的是specs2 + mockito。
业务代码
class Endpoints(...) extends ... with ABC_ExecutionContext with FutureSupport { protected implicit def executor = executionContext val myEndpoint: Route = asyncPost("/endpoint/", operation(queryGroup)) { setTransactionName(":post /endpoint") new AsyncResult { val is = { for { ... out <- doSomethingWithRequest() // 隐式传递request,返回Future ... response <- doSomething(out) // 返回Future } yield response }} } }
测试代码(原始版本)
class EndpointsSpec extends MockitoSugar with JsonFormats with ... { "POST /endpoint on Endpoints" should { "should work for request A" in { post("/endpoint/", write(mockRequestA).getBytes(), Map("user-id" -> "12345")) { body must_== """something A""" status must_== 200 } } "should work for request B" in { post("/endpoint/", write(mockRequestB).getBytes(), Map("user-id" -> "12345")) { body must_== """something B""" status must_== 200 } } } }
测试代码(添加Hystrix上下文版本)
class EndpointsSpec extends MockitoSugar with JsonFormats with ... { "POST /endpoint on Endpoints" should { "should work for request A" in { val context: HystrixRequestContext = HystrixRequestContext.initializeContext try { post("/endpoint/", write(mockRequestA).getBytes(), Map("user-id" -> "12345")) { body must_== """something A""" status must_== 200 } } finally { context.shutdown() } } "should work for request B" in { val context: HystrixRequestContext = HystrixRequestContext.initializeContext try { post("/endpoint/", write(mockRequestB).getBytes(), Map("user-id" -> "12345")) { body must_== """something B""" status must_== 200 } } finally { context.shutdown() } } } }
排查方向与解决方案
1. 显式传递Request,避免隐式上下文串扰
Scalatra的隐式Request绑定在当前线程,而Future会在其他线程执行,若依赖ThreadLocal传递的隐式Request,很容易出现上下文串混。建议显式把Request传入业务方法:
// 修改业务代码中的调用 out <- doSomethingWithRequest(request) // 显式传递request参数
2. 强制测试串行执行
specs2默认可能并行执行测试,异步操作的上下文未及时清理会导致串扰。给测试类添加sequential特质,强制测试按顺序执行:
class EndpointsSpec extends MockitoSugar with JsonFormats with ... with sequential { // 新增串行执行特质 // ... 测试代码 }
3. 隔离ExecutionContext
业务代码使用的全局ABC_ExecutionContext可能导致不同测试的任务混跑。测试时为每个测试创建独立的ExecutionContext并注入:
"should work for request A" in { val testEC = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor()) val endpoints = new Endpoints(..., testEC) // 假设构造函数支持传入ExecutionContext // ... 测试逻辑 }
4. 确保异步测试完成后再断言
显式使用await包裹异步断言,避免测试提前结束导致上下文未清理:
post("/endpoint/", ...) { await(body must_== """something A""") await(status must_== 200) }
内容的提问来源于stack exchange,提问作者Navnav
相关产品推荐
相关产品推荐

