简体   繁体   English

我们如何使用通用返回类型和隐式参数模拟scala方法?

[英]How can we mock scala method with generic return type and implicit parameters?

I have a configuration fetcher that looks something like this. 我有一个看起来像这样的配置访存器。

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")
}

The invocation of the same method is as follows - 相同方法的调用如下:

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

Now I want to mock the getForCountry method in my unit tests - 现在,我想在单元测试中模拟getForCountry方法-

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

Surprisingly it seems that this expectation is not set up properly. 令人惊讶的是,这种期望似乎设置不正确。 On executing the test the mock returns null. 在执行测试时,模拟返回null。

Any clues on how to proceed with this? 关于如何进行此操作的任何线索? Please feel free to let me know if you need any further details. 如果您需要任何其他详细信息,请随时告诉我。

I strongly doubt that different instances of implicit ConfigLoader and AppContext are getting passed in your actual method invocation and mocked one. 我强烈怀疑隐式ConfigLoaderAppContext不同实例是否在您的实际方法调用中被传递ConfigLoader If you are using intellij, verify which implicits are getting passed by enabling them. 如果使用的是intellij,请通过启用它们来验证传递了哪些隐式对象。 To enable them, press ctr+alt+shift++ 要启用它们,请按ctr+alt+shift++

Here is the complete tests simulates your situation which works just fine: 这是完整的测试,可以很好地模拟您的情况:

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)

Thanks a ton Pritam. 感谢吨Pritam。 The following code seems to work. 以下代码似乎有效。

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)

Have you tried mockito-scala ? 您是否尝试过的Mockito,斯卡拉 if you use the new syntax the implicits will be taken care of automatically (assuming you use the idiomatic syntax and the same instances are resolved in the test and in your prod code) 如果您使用新语法,则隐式将被自动处理(假定您使用惯用语法,并且在测试和产品代码中解析了相同的实例)

Also even if you use the traditional syntax your stub would be reduced to 同样,即使您使用传统语法,您的存根也会减少到

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

or with the idiomatic syntax 或使用惯用语法

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

or if the implicits are not the same in test and prod (notice I can also mix arg matchers like * and raw params like 'false') 或者如果隐式在测试和生产中是不同的(请注意,我也可以将arg匹配器(如*)和原始参数(如'false')混合使用

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM