Search code examples
scalascalatestscalamock

How to set up a ScalaMock once for repeated method invocations?


I want to set up a test in Scala that creates a mock config to provide certain values. I'm using ScalaTest 3.0.1, ScalaMock 3.4.2, and typesafe 1.3.1. The goal is to mock values for the config before running tests. The docs at http://www.scalatest.org/user_guide/testing_with_mock_objects and http://scalamock.org/user-guide/features/ seem to offer a couple options. First, here's some target code:

import com.typesafe.config.Config
class Sample(config: Config) {
    private val aValue = config.getDouble("aValue")
}

It seems like it should be possible to set everything up one time, or to set up everything before each test. This attempt fails:

class SampleSpec extends FlatSpec with MockFactory with BeforeAndAfterAll {

  private val mockConfig = mock[Config]

  override def beforeAll {
    (mockConfig.getDouble _).expects("aValue").returning(1.0).anyNumberOfTimes()
  }

  "testOne" should "return 1" in {
    new Sample(mockConfig)
  }

  "testTwo" should "return 1" in {
    new Sample(mockConfig)
  }

}

The first test succeeds but second test in the fixture fails, and this error is produced:

Unexpected call: <mock-1> Config.getDouble(aValue)

Expected:
inAnyOrder {

}

Actual:
  <mock-1> Config.getDouble(aValue)
ScalaTestFailureLocation: scala.Option at (Option.scala:120)
org.scalatest.exceptions.TestFailedException: Unexpected call: <mock-1> Config.getDouble(aValue)

Expected:
inAnyOrder {

}

Here's an alternative approach:

class SampleSpec extends FlatSpec with MockFactory with BeforeAndAfter {

  private val mockConfig = mock[Config]

  before {
    (mockConfig.getDouble _).expects("aValue").returning(1.0)
  }

  "testOne" should "return 1" in {
    new Sample(mockConfig)
  }

  "testTwo" should "return 1" in {
    new Sample(mockConfig)
  }

}

It produces this exception:

An exception or error caused a run to abort: assertion failed: Null expectation context - missing withExpectations?
java.lang.AssertionError: assertion failed: Null expectation context - missing withExpectations?

Why does the first attempt fail? The test specifies that getDouble can be invoked any number of times, yet the second test fails as if anyNumberOfTimes() wasn't used. How should this be coded so that the method can be mocked once and invoked repeatedly? Why does the second attempt fail? Is there a method to reset the mock so it can be reused?


Solution

  • I'd also like to point out the documentation page for this, with a slightly different style (using a trait):

    http://scalamock.org/user-guide/sharing-scalatest/#fixture-contexts

    For example:

    class SampleSpec extends FlatSpec with OneInstancePerTest with MockFactory {
    
      private val mockConfig = mock[Config]
    
      (mockConfig.getDouble _).expects("aValue").returning(1.0).anyNumberOfTimes
    
      "testOne" should "return 1" in {
        new Sample(mockConfig)
      }
    
      "testTwo" should "return 1" in {
        new Sample(mockConfig)
      }
    
    }