1
votes

I need some help with how to mock higher order functions inside classes using ScalaMock

import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}

class TestSpec extends FlatSpec with MockFactory with Matchers {

  class Foo {
    def foo(f: () ⇒ String) = "this is foo"
  }

  val fooMock = mock[Foo]

  "Failing test" should "pass but doesn't" in {
    (fooMock
      .foo(_: () ⇒ String))
      .expects({ () ⇒
        "bar"
      })
      .returns("this is the test")

    // will fail here
    val result = fooMock.foo({ () ⇒
      "bar"
    })

    assert(true)
  }

  "Passing test" should "that passes but I can't do it this way" in {
    val f = { () ⇒
      "bar"
    }

    (fooMock.foo(_: () ⇒ String)).expects(f).returns("this is the test")

    val result = fooMock.foo(f)

    result shouldBe "this is the test"
  }
}

As you can see with my code above, the function being mocked works fine when you pass in a value that has the higher order function, but won't if you type it out each spot. In my use case, I can't do it the way I do it in the second test

Below is more information on the use case but not completely necessary to answer this question

This is a simplified example, but I need a way to get the former to work. Reason being (I'll try my best to explain this) I have a class A that is being tested. Inside A is a function that is passed a mocked class B, and basically the foo function (as seen below) is inside this mocked B and I can't just pass in f like I do in the second example below. If this didn't make any sense, I can try to replicate this exactly.

TL;DR I need the first test to work lol

Any ideas why this might be happening?

If you are curious as to why I need to do this, here is a more exact example with how I am using this:

import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}

class TestSpec extends FlatSpec with MockFactory with Matchers {

  class Foo {
    def foo(f: () ⇒ String) = s"you sent in: ${f()}"
  }

  object Bar {

    def bar(foo: Foo): String = {
      val f = { () ⇒ "bar" }

      foo.foo(f)
    }
  }

  val fooMock = mock[Foo]

  "Failing test" should "pass but doesn't" in {
    (fooMock.foo(_: () ⇒ String))
      .expects({ () ⇒
        "bar"
      })
      .returns("this is the test")

    // will fail here
    val result = Bar.bar(fooMock)

    assert(true)
  }
}
1

1 Answers

0
votes

The reason why this doesn't work comes down to Scala itself. e.g. see this scala repl:

scala> val f = { () ⇒ "bar" }
f: () => String = $$Lambda$1031/294651011@14a049f9

scala> val g = { () ⇒ "bar" }
g: () => String = $$Lambda$1040/1907228381@9301672

scala> g == f
res0: Boolean = false

So ScalaMock cannot compare functions for equality either without help. What I would suggest is, if you are interested in the result of the function only, to use predicate matching, as described in the documentation here: http://scalamock.org/user-guide/matching/

In the predicate, you could evaluate the captured parameter and check that the return value is indeed bar. Or, if your function is more complicated, maybe you can even do a mock[() => String] and compare reference equality in the matcher?