Будущее .recover не вызывается, когда в модульном тесте Mockito генерируется Exception - PullRequest
3 голосов
/ 13 марта 2019

Следующий код возвращает Future.

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

Затем я обрабатываю Future

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

fineOne возвращает вызов 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
    }
  }

Я полагаю, что восстановление будет вызвано, если Future, возвращенный findOne, завершится неудачей, т.е. выдаст исключение.Поэтому я имитирую это, заставляя getOneById выдавать исключение.

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

Но модульный тест не вызывает исключение, и тест продолжается с использованием значения Future(Some(User)).

Iтакже пытался выдать исключение из findOne - when(mockUserRepository.findOne(userKeys)).thenThrow(classOf[RuntimeException]), но тестовый пример останавливается со следующими двумя отпечатками, и .recover из Future не называется

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

Ответы [ 2 ]

1 голос
/ 13 марта 2019

Это findUserFuture: Future[Option[User]] или userRepo.findOne возвращает будущее, следовательно, вам нужно вернуть 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
  }
  • Обновление * Внимательно изучив исходный пост, я обнаружил небольшую ошибку в том, как вы издеваетесь. попробуйте ниже:
when(mockUserRepository.findOne(userKeys)).thenCallRealMethod()
when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) 

Обратите внимание thenCallRealMethod() здесь, ранее вы насмехались findOne, чтобы вернуть будущее с успешным значением, что означает, что оригинальный метод не вызывался, который в свою очередь не вызывал getOneById

0 голосов
/ 13 марта 2019

Вы не можете смоделировать тип, который хотите протестировать, макет не имеет ничего от исходного поведения этого типа.

Если вы хотите заглушить только некоторое поведение тестируемого класса (или любого другого типа), вы должны использовать шпиона, тогда вы только сделаете

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

, затем позвоните spyUserRepository.findOne(userKeys) и подтвердите, что он возвращает ошибочное будущее

При этом, похоже, у вас есть ответственностьнемного запутавшись здесь, я бы посоветовал еще раз взглянуть на ваш дизайн, поскольку мне приходится прибегать к использованию шпионов, потому что для меня это выглядит как большой запах кода.

...