Play 2.6测试ProjectController时出现NullPointerException求助
解决Play 2.6 + ReactiveMongo控制器实例化测试的NullPointerException问题
你的问题出在测试中对collection方法的mock方式上——直接mockFuture[JSONCollection]会导致链式调用时触发空指针,因为Mockito生成的Future mock并没有实现核心的异步方法。下面是具体的分析和修复方案:
问题根源
你在测试里写了:
override def collection: Future[JSONCollection] = mock[Future[JSONCollection]]
但Future是一个异步trait,Mockito创建的mock对象并不会自动实现flatMap这类方法。当控制器代码调用collection.flatMap(...)时,就会因为未初始化的方法抛出NullPointerException。另外,你也没有对集合的后续操作(find/cursor/collect)做正确的mock,导致整个调用链断裂。
修复后的测试代码
我们需要mockJSONCollection对象,并用Future.successful包装它,然后依次mock链式调用的每一步,让它们返回预期的测试数据:
class ProjectControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting with MockitoSugar { val mockedReactiveMongoApi: ReactiveMongoApi = mock[ReactiveMongoApi] val mockedExecutionContext: ExecutionContext = mock[ExecutionContext] "ProjectController GET" should { "render the 'List of Projects' page from a new instance of controller" in { // 1. 准备测试用的Project列表(根据你的实际case class调整) val testProjects = List( Project("proj-1", "My First Project"), Project("proj-2", "My Second Project") ) // 2. Mock ReactiveMongo的各个核心对象 val mockedCollection: JSONCollection = mock[JSONCollection] val mockedFindObservable: FindObservable[JsObject] = mock[FindObservable[JsObject]] val mockedProjectedObservable: FindObservable[JsObject] = mock[FindObservable[JsObject]] val mockedCursor: Cursor[Project] = mock[Cursor[Project]] // 3. 配置mock对象的行为,模拟完整的查询链 when(mockedCollection.find(Json.obj())).thenReturn(mockedFindObservable) when(mockedFindObservable.projection(Json.obj())).thenReturn(mockedProjectedObservable) when(mockedProjectedObservable.cursor[Project]()).thenReturn(mockedCursor) when(mockedCursor.collect[List](-1, Cursor.FailOnError[List[Project]]())) .thenReturn(Future.successful(testProjects)) // 4. 创建控制器实例,重写collection返回已准备好的mock集合 val controller: ProjectController = new ProjectController(stubControllerComponents(), mockedReactiveMongoApi)(mockedExecutionContext) { override def collection: Future[JSONCollection] = Future.successful(mockedCollection) } // 5. 执行测试断言 val result = controller.listProjects().apply(FakeRequest(GET, "/projects")) status(result) mustBe OK contentType(result) mustBe Some("text/html") contentAsString(result) must include ("Projects") } } }
额外提示
- 如果不需要验证
find或projection的具体参数,可以用any()替代Json.obj(),比如when(mockedCollection.find(any[JsObject])).thenReturn(mockedFindObservable),让测试更灵活。 - 确保你的
Projectcase class有对应的JSON格式定义(比如implicit val projectFormat: OFormat[Project]),否则ReactiveMongo的cursor[Project]会出错。 - 永远不要直接mock
Future、Option这类基础容器类型,而是用它们的工厂方法(比如Future.successful、Some)来包装mock对象,这样容器的方法才能正常工作。
内容的提问来源于stack exchange,提问作者consuela
相关产品推荐
相关产品推荐

