【问题标题】:Scala mock when/thenReturn Future nullScala 模拟 when/thenReturn Future null
【发布时间】:2021-05-05 10:43:08
【问题描述】:

我正在尝试使用 scalatestplus-play(4.0.3) 测试我的代码。我也使用 mockito-core(3.7.7)。我的测试代码不起作用。它以异常结束;

[info]   java.lang.NullPointerException: Cannot invoke "scala.concurrent.Future.flatMap(scala.Function1, scala.concurrent.ExecutionContext)" because "this.future" is null
[info]   at core.utils.Extensions$GetOrFail.getOrFailWith(Extensions.scala:12)
[info]   at services.ShowcaseService.update(ShowcaseService.scala:42)
[info]   at services.ShowcaseServiceSpec.$anonfun$new$5(ShowcaseServiceSpec.scala:59)
[info]   at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   at org.scalatest.WordSpecLike$$anon$3.apply(WordSpecLike.scala:1075)

也是我的测试代码;

    val showcase = Showcase("id", "title", None, StrategyType.manuel, active = false, RecordTime())
    val updatedShowcase = Showcase("id", "titleChange", Option("descChange"), StrategyType.manuel, active = false, RecordTime())

    val showcaseUpdateRequest: ShowcaseUpdateRequest = ShowcaseUpdateRequest(Option("titleChange"), Option("descChange"))

    val id = "id"
    when(showcaseRepository.findOneById(id)).thenReturn(Future(Some(showcase)))
    when(showcaseRepository.update(showcaseUpdateRequest, id)).thenReturn(Future(updatedShowcase))

    val result = showcaseService.update(showcaseUpdateRequest, id)

展示服务;

  def update(req: ShowcaseUpdateRequest, id: ShowcaseId): Future[Showcase] = for {
    _ <- showcaseRepository.findOneById(id).getOrFailWith(ShowcaseNotFoundException(id))
    updatedShowcase <- showcaseRepository.update(req, id)
  } yield updatedShowcase

最后是发生错误的隐式类;

  implicit class GetOrFail[T](future: Future[Option[T]]) {
    def getOrFailWith[E <: Throwable](ex: E)(implicit ec: ExecutionContext): Future[T] = future flatMap {
      case None => Future.failed(ex)
      case Some(t) => Future.successful(t)
    }
  }

【问题讨论】:

  • 您能否说明您在哪里创建了GetOrFail 类?看来你是用null 创建的
  • @TomerShetah 我已经在描述中展示了它并隐式创建了它。
  • 仍然缺少可以帮助您的详细信息。你能添加一个完整的测试吗?我们需要一个minimal reproducible example
  • @TomerShetah 你能再检查一下吗?

标签: scala playframework mockito scalatest


【解决方案1】:

根据堆栈跟踪问题发生是因为模拟的showcaseRepository.update 返回null,这是因为每当任何模拟的参数与实际接收的参数不匹配时,mockito 都会返回null

其中一种方法是:使用doAnswerany 返回Future.failed 以获取任何未通过测试的意外参数:

// Leave for expected result
when(showcaseRepository.update(showcaseUpdateRequest, id)).thenReturn(Future(updatedShowcase))

// For all unexpected arguments
doAnswer(args => Future.failed(new Exception("Unexpected arguments for `update`: $args"))).when(showcaseRepository).update(any, any)

【讨论】:

    【解决方案2】:

    您似乎从showcaseRepository.findOneById(id) 获得null

    最有可能发生这种情况是因为使用不同的参数,即你给模拟的东西(看起来你在那里进行了一些隐式转换,因为你的 id 是一个字符串,但函数似乎想要 @ 987654324@).

    所以,当你这样做时: when(showcaseRepository.findOneById(id)).thenReturn(Future(Some(showcase))) 它基本上说“当findOneById 被调用时使用这个确切的参数,我希望它返回这个值”。

    但是根据您的隐式转换是如何实现的,您最终调用该函数时使用的 ShowcaseId 的值可能最终与您设置模拟时的值不同。

    也可能是这样,您的代码做错了什么,并使用错误的参数值调用函数,因此测试正确失败,只是以难以解释的方式失败。

    因此,最好不要在存根调用时使用实际参数值,而是在之后使用verify 它们:

    when(showcaseRepository.findOneById(any)).thenReturn(Future(Some(showcase))
    
    ... // do the test
    
    verify(showcaseRepository).findOneById(id)
    

    此设置将使findOneById 总是 返回该值,而不管其参数如何,然后,如果该函数实际上是使用不同的参数值调用的,它将使verify 失败一个有意义的消息(比如“用这个值而不是那个值调用”)而不是一个神秘的 NPE。

    附带说明,Future.successful(Some(showcase)) 是一种更好的方式来创建即时期货。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-18
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      • 2017-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多