Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Future's .recover not getting called when Exception is thrown in Mockito unit test

The following code returns a Future.

val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys) 

Then I process the Future

findUserFuture.flatMap {....}
.recover{...}

fineOne returns the Future and the Future wraps call to getOneById

def findOne(userKeys:UserKeys):Future[Option[User]] = {
    Future{
      //val loginInfo:LoginInfo = LoginInfo(userKeys.providerID,userKeys.authProvider)
      val userOption:Option[User] = getOneById(userKeys)
      userOption
    }
  }

I suppose that recover will be called if Future returned by findOne fails i.e. throws an Exception. So I am simulating that by making getOneById throw an exception.

when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user)))
      when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) //simulating database error

But the unit test doesn't throw an exception and the test proceeds using value Future(Some(User)).

I also tried throwing the exception from findOne - when(mockUserRepository.findOne(userKeys)).thenThrow(classOf[RuntimeException]) but the test case stops with the following two prints and the .recover of the Future is not called

java.lang.RuntimeException was thrown.
java.lang.RuntimeException
like image 232
Manu Chadha Avatar asked Oct 16 '22 05:10

Manu Chadha


1 Answers

This findUserFuture: Future[Option[User]] or userRepo.findOne returns future, hence you need to return Future.failed in your mock. For ex.

when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user)))
when(mockUserRepository.getOneById(userKeys)).thenReturn(Future.failed(new RuntimeException("network failure"))

Find below complete working test to simulate your use case :

test("mock future test") {
    case class User(name: String)
    case class UserNotFoundException(name: String) extends Exception
    trait UserRepo {
      def findOne(name: String): Future[Option[User]]
    }
    val name      = "bob"
    val dummyUser = User("dummy")

    val userRepo = mock[UserRepo]
    when(userRepo.findOne(name)).thenReturn(Future.failed(new RuntimeException()))

    val userF = userRepo
      .findOne(name)
      .flatMap {
        case Some(user) ⇒ Future.successful(user)
        case None       ⇒ Future.failed(UserNotFoundException(name))
      }
      .recover {
        case NonFatal(_) ⇒ dummyUser
      }

    userF.futureValue shouldBe dummyUser
  }
  • Update * After looking at the original post closely, I found small mistake in the way you are mocking. try below:
when(mockUserRepository.findOne(userKeys)).thenCallRealMethod()
when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) 

Notice thenCallRealMethod() here, earlier you were mocking findOne to return future with successful value which means original method was not getting called which in turn was not calling getOneById

like image 174
Pritam Kadam Avatar answered Oct 27 '22 10:10

Pritam Kadam