Reputation: 119
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 -
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.
Any clues on how to proceed with this? Please feel free to let me know if you need any further details.
Upvotes: 0
Views: 1366
Reputation: 1497
Have you tried mockito-scala? 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')
configurationHelper.getForCountry[Map[String, String]]
("googleCloudPlatform.jobConfig.demandBasedPricing",false)(*,*)
shouldReturn countryPricingWeekConfiguation
Upvotes: 1
Reputation: 119
Thanks a ton 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)
Upvotes: 0
Reputation: 2527
I strongly doubt that different instances of implicit ConfigLoader
and AppContext
are getting passed in your actual method invocation and mocked one.
If you are using intellij, verify which implicits are getting passed by enabling them. To enable them, press 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)
Upvotes: 1