0

I have a case class

case class InputCriteria(a: Int) {
   val b: Int = config.getInt("some path")
}

How to mock this case class and override the value of b?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • 5
    I mean, the way you asked the question what I'm going to say is not an answer, but you are assuming the way you've written that case class is a good approach. Why don't you just leave the case class be and make something else create classes based on configuration values. You should probably never need to mock a case class. – fd8s0 May 20 '19 at 15:47
  • I need to anyhow override the config value as I am not using any config file for test. So I think it is same whether I put it in the case class or outside case class. Please feel free to correct me. – Shantiswarup Tunga May 21 '19 at 07:19
  • it's not the same, because whatever parses your configuration can be outside of this particular test and you can just instantiate the case class and pass it to this test, I think you are making your life hard for no reason – fd8s0 May 21 '19 at 16:24

2 Answers2

5

From scalamock faq:

Can I mock val / lazy val?

No, the Scala compiler will not allow overriding a val with a def, so with ScalaMock this is not possible. An option we are looking at for a possible future version of ScalaMock is scala.meta, but this is not yet available for all build targets we want. If you can, it is better to design a trait with a def and mock that instead. The concrete implementation can still override that def with a val to give invariant behaviour.

If you change your case class to trait you'd be able to override val with proxy.MockFactory.

If you changed you val to def you'd be able to override with plain mock.

You could also use the approach from Raman's answer, so unless you would want to make your class final it's working solution.

But what you should really do in my opinion is just creating trait:

trait InputCriteria {
     def b: Int
}

and then implementing it:

case class ConfigDrivenInputCriteria(config: Config) extends InputCriteria {
    override val b: Int = config.getInt("some path")
}

Then in test you could just reimplement it:

val testInputCritria = new InputCriteria {
    override def b: Int = 4
}

but it might be a little bit clunky if you've got a lot of fields in InputCriteria, but in that case you could also mock it:

val inputCriteria = stub[InputCriteria]
(inputCriteria.b _).when().returns(100)

Interface + implementation approach gives you the ability to test your code easily. You can also decide in implementing class if your properties should be defs, vals or lazy vals.

Community
  • 1
  • 1
Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
  • I agree with your approach. But I will go for trait if there are more input-criteria. In my case I need only one input criteria and creating a trait will add extra code which is not useful for other part of application. Please feel free to correct me. – Shantiswarup Tunga May 21 '19 at 07:12
  • 2
    You're correct, the only reason for the existence of that trait is to allow easier testing. But in my opinion, it is worth to pay that price, because then mocking dependencies is so much simpler. – Krzysztof Atłasik May 21 '19 at 07:40
2

try this:

object abc extends App {
  case class A() {
    val x = 6
  }

  val a: A = new A() {
    override val x = 9
  }

  println(A().x, a.x)
}
Raman Mishra
  • 2,635
  • 2
  • 15
  • 32