Search code examples
scalamockitocouchbasefuturespecs2

How do you mock a future that will work with map function?


I'm trying to do unit test for the following couchbase method call, I couldn't find a satisfactory sample here in SO, so I thought I'd post a question....

def getUserSession(token: String, ipAddr: String, userAgent: Option[String]): Future[Option[UserSession]] = {
    val query = new Query().setIncludeDocs(true).setLimit(1)
      .setRangeStart(ComplexKey.of(token))
      .setRangeEnd(ComplexKey.of(s"$token\uefff"))
      .setStale(Stale.FALSE)
    bucket.find[UserSession](DOC_NAME, VIEW_BY_TOKEN)(query).map(_.headOption) map {
      userSessionOpt => {
    userSessionOpt filter {
      userSession =>
        (userSession.ipAddr == ipAddr) &&
          (!userAgent.isDefined || !userSession.userAgent.isDefined || userSession.userAgent == userAgent)
    }
  }
}

}

So this is my attempt, my unit test excerpt:

  val mockQueryResult = mock[Future[List[UserSession]]]
  val mockUserSessionList = mock[List[UserSession]]
  val mockUserSession = mock[UserSession]

  // This is just my trial and erros
  mockUserSessionList.head returns mockUserSession
  mockUserSessionList(0) returns mockUserSession

  Await.result(mockQueryResult, Duration(60, SECONDS)) returns mockUserSessionList

  mockBucket.find[UserSession](any[String], any[String])(any[Query])(any[Reads[UserSession]], any[ExecutionContext]) returns mockQueryResult

  val queryCaptor = capture[Query]
  val readsCaptor = capture[Reads[UserSession]]
  val executionContextCaptor = capture[ExecutionContext]
  val docNameCaptor = capture[String]
  val viewNameCaptor = capture[String]

  userSessionRepositoryWithMockBucket.getUserSession(TEST_SESSION_TOKEN1, TEST_IP_ADDR1, TEST_USER_AGENT)

  there was one(mockBucket).find[UserSession](docNameCaptor, viewNameCaptor)(queryCaptor)(readsCaptor, executionContextCaptor)

Now at some point in the bucket.find method call is throwing NPE. I did a little digging and it seems like the Await.result returns mocking doesn't really work in returning value for map probably for obvious reasons. It does return result if I modified my getUserSession function to use Await.result. I was just trying it out 'cause I haven't creatively figured out other ways (this is new to me).

There seems to be a less verbose ways of doing the mocking instead of having to do all the layers of Future[List[UserSession]]. Thanks for your time!


Solution

  • I think I might've found the answer to how to mock future that work with map operation. Seems like if we create a 'successful' Future, it will work as follows:

      val mockUserSession = createTestUserSession
      val mockUserSessionList = mock[List[UserSession]]
      val mockUserSessionListFuture = Future.successful[List[UserSession]](mockUserSessionList)
    
      mockUserSessionList.headOption returns Some(mockUserSession)
    

    This will in turn allows the map to in the getUserSession to be supplied with proper instance of the headOption which in this case is Some(mockUserSession)