【问题标题】:How can we mock scala method with generic return type and implicit parameters?我们如何模拟具有泛型返回类型和隐式参数的 scala 方法?
【发布时间】:2019-03-17 05:32:15
【问题描述】:

我有一个看起来像这样的配置提取器。

def getForCountry[A](path: String, fallbackToDefault: Boolean)
                  (implicit loader: ConfigLoader[A], ac: AppContext): A = {
configuration.getOptional[A](s"${ac.country}.$path") match {
  case Some(value)                =>
    value
  case None if fallbackToDefault  =>
    configuration.get[A](path)
  case None if !fallbackToDefault =>
    throw new RuntimeException(s"${ac.country}.$path key not found in configuration")
}

同一个方法的调用如下-

val countrySpecificConfig =
  configurationHelper.getForCountry[Map[String, String]]("googleCloudPlatform.jobConfig.demandBasedPricing", fallbackToDefault = false)

现在我想在我的单元测试中模拟 getForCountry 方法 -

when(configurationHelper
    .getForCountry[Map[String, String]]("googleCloudPlatform.jobConfig.demandBasedPricing", fallbackToDefault = false))
    .thenReturn(countryPricingWeekConfiguation)

令人惊讶的是,这种期望似乎没有正确设置。在执行测试时,模拟返回 null。

关于如何进行此操作的任何线索?如果您需要更多详细信息,请随时告诉我。

【问题讨论】:

    标签: scala unit-testing playframework mockito expectations


    【解决方案1】:

    我强烈怀疑隐式 ConfigLoaderAppContext 的不同实例是否会在您的实际方法调用中传递并模拟一个。 如果您使用的是 intellij,请通过启用它们来验证哪些隐式正在通过。要启用它们,请按ctr+alt+shift++

    这是模拟您的情况的完整测试,效果很好:

    test("mock example") {
        trait ConfigLoader[T] {}
    
        trait AppContext { def country: String }
    
        trait ConfigurationHelper {
          def getForCountry[A](x: String, fallbackToDefault: Boolean = true)(implicit loader: ConfigLoader[A], ac: AppContext): A
        }
    
        implicit val loader: ConfigLoader[Map[String, String]] = mock[ConfigLoader[Map[String, String]]]
        implicit val ctx: AppContext                           = mock[AppContext]
        val configurationHelper                                = mock[ConfigurationHelper]
    
        val mockedResult = Map("x" → "1")
    
        when(
          configurationHelper
            .getForCountry[Map[String, String]]("googleCloudPlatform.jobConfig.demandBasedPricing", fallbackToDefault = false)
        ).thenReturn(mockedResult)
    
        val countrySpecificConfig =
          configurationHelper
            .getForCountry[Map[String, String]]("googleCloudPlatform.jobConfig.demandBasedPricing", fallbackToDefault = false)
    
        countrySpecificConfig.foreach(println)
      }
    
    // =========================== Output ====================
    // (x,1)
    
    

    【讨论】:

      【解决方案2】:

      你试过mockito-scala吗?如果您使用新语法,则隐式将自动处理(假设您使用惯用语法并且在测试和产品代码中解析了相同的实例)

      此外,即使您使用传统语法,您的存根也会被缩减为

      when(configurationHelper
          .getForCountry[Map[String, String]]
          (eqTo("googleCloudPlatform.jobConfig.demandBasedPricing"), eqTo(false))(*, *)
          .thenReturn(countryPricingWeekConfiguation)
      

      或使用惯用语法

      configurationHelper.getForCountry[Map[String, String]]
          ("googleCloudPlatform.jobConfig.demandBasedPricing",false)
          shouldReturn countryPricingWeekConfiguation
      

      或者如果隐式在 test 和 prod 中不同(注意我也可以混合使用 arg 匹配器,如 * 和原始参数,如 'false')

      configurationHelper.getForCountry[Map[String, String]]
          ("googleCloudPlatform.jobConfig.demandBasedPricing",false)(*,*)
          shouldReturn countryPricingWeekConfiguation
      

      【讨论】:

        【解决方案3】:

        非常感谢普里塔姆。以下代码似乎有效。

        when(configurationHelper
            .getForCountry[Map[String, String]]
            (ArgumentMatchers.eq("googleCloudPlatform.jobConfig.demandBasedPricing"), ArgumentMatchers.eq(false))
            (ArgumentMatchers.any[ConfigLoader[Map[String, String]]](), ArgumentMatchers.any[AppContext]()))
            .thenReturn(countryPricingWeekConfiguation)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-15
          • 2012-11-01
          • 2021-02-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多