在 Mockito 单元测试中抛出异常时未调用 Future 的 .recover

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

下面的代码returns一个Future.

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

然后我处理 Future

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

fineOne returns FutureFuture 结束对 getOneById

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

我想如果 findOne 返回的 Future 失败,即抛出异常,将调用恢复。所以我通过让 getOneById 抛出异常来模拟。

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

但是单元测试没有抛出异常,测试继续使用值 Future(Some(User))

我也尝试从 findOne - when(mockUserRepository.findOne(userKeys)).thenThrow(classOf[RuntimeException]) 抛出异常,但测试用例停止了 使用以下两个打印件并且 Future.recover 未被调用

java.lang.RuntimeException was thrown.
java.lang.RuntimeException

您无法模拟要测试的类型,模拟没有该类型的原始行为。

如果你只想存根class被测(或任何其他类型)的某些行为,你应该使用间谍,那么你只需要做

when(spyUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException])

然后调用 spyUserRepository.findOne(userKeys) 并断言它 returns 一个失败的未来

就是说,您的职责似乎有点混乱,我建议您重新审视一下您的设计,因为我不得不求助于间谍程序,因为这对我来说是一种很大的代码味道。

这个findUserFuture: Future[Option[User]]userRepo.findOnereturn的未来, 因此你需要 return Future.failed 在你的模拟中。 对于前。

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

在下面找到完整的工作测试来模拟您的用例:

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
  }
  • 更新 * 仔细查看原始 post 后,我发现您的嘲笑方式存在小错误。 尝试以下:
when(mockUserRepository.findOne(userKeys)).thenCallRealMethod()
when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) 

注意这里的thenCallRealMethod(),早些时候你用成功的值模拟了findOne到return的未来,这意味着原始方法没有被调用,这反过来又没有调用getOneById