Search code examples
scalascalatestcase-classstubbingscalamock

Scalamock cannot differentiate the Futures


I have a piece of a scala code in class A.scala There is a case class Case1 which contains a field Future of f1 which is of type of another case class Case2. Case2 encloses Seq[String]. I send the case2 object to another class B whose instance is b

case class Case2(list: Seq[String])
case class Case1(f1: Future[Case2])

class A(b: B) {
   def doSomething() {
     val case1 = Case1(Future(Case2(List("Hello")))
     val result = b.doSomethingElse(case1)   // Another future returned
     result
  }
}

class ATest extends .... Some scalatest libraries {
    val bMock = mock[B]
    val a = new A(bMock)
    "A" should {
        "call b" in  {
            val case1 = Case1(Future(Case2(List("Hello")))
            val result = .....Anything....
            (b.doSomethingElse _).expects(case1).returning(Future.successful(result))
             a.doSomething().futureValue shouldBe .....Something
      }
    }
 }

The test fails with a message that the mock call to Class B does not match with actual. It prints Expected and Actual but they both are looking same in the log.

Ideally the test should pass as the mock call to B matches with actual call of B. But I suspect that it is because Case1 encloses a Future, which it considers as a different object. When I substitute wild card i.e (b.doSomethingElse _).expects(*).returning(result).

Is there a way to make pass this test ? I use scalaMock for the mocking purpose.


Solution

  • You cannot reliably check for equality any function or ongoing computation, so with:

    • Futures
    • Task
    • Free monads
    • DBIO
    • Function types

    just forget about using marchers on them (both for assertions and for mocking).

    For assertions the only reliable thing you can do is to run it/materialize the result and delay assertion until you obtain some actual value.

    You code fails because in your mock you match against some Future value (which, again, you cannot reliably compare against and mocks internally use some == to know if now is the moment to return mocked value).

    So instead try to accept any value

    (b.doSomethingElse _).expects(*).returning(Future.successful(result))
    

    if that is unacceptable in your case you might want to replace expects(*) with something like expects(where(future => Await.result(future) == something)).