在 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 Future
和 Future
结束对 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.findOne
return的未来,
因此你需要 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
下面的代码returns一个Future.
val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys)
然后我处理 Future
findUserFuture.flatMap {....}
.recover{...}
fineOne
returns Future
和 Future
结束对 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.findOne
return的未来,
因此你需要 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