Search code examples
scalamockitobddscalatest

Scala FunSpec tests describe() initialization and execution order


I faced with a problem when try to use BDD approach with scalatest and mockito. To reduce code duplication I put each needed when() rule inside each of describe blocks. But I was surprised by the ordering how describe() blocks run.

class SomeTest extends FunSpec with BeforeAndAfterAll with MockitoSugar {

  private val catalogClient = mock[CatalogServiceClient]

  override def beforeAll {
      when(catalogClient.getFrame(any)).thenReturn(Frame())
  }

  describe("MyTest1") {
    println("Inside MyTest1")

    when(catalogClient.getConnection(any))
      .thenReturn(Conn(ID_FOR_TEST_1))

    it("should perform action with data ID_FOR_TEST_1") {
      println("Inside it 1")
    }

    it("should perform another action with data ID_FOR_TEST_1") {
      ///
    }

  }

  describe("MyTest2") {
    println("Inside MyTest2")

    when(catalogClient.getConnection(any))
      .thenReturn(Conn(ID_FOR_TEST_2))

    it("should perform logic with data ID_FOR_TEST_2") {
      println("Inside it 2")
    }

    it("should perform another logic with data ID_FOR_TEST_2") {
      ///
    }
  }
}

It printed:

"Inside MyTest1"
"Inside MyTest2"
"Inside it 1"
"Inside it 2"

while I was expecting

"Inside MyTest1"
"Inside it 1"
"Inside MyTest2"
"Inside it 2"

And the first test failed because mocked data replaced in the second describe() block.

So it firstly goes through all describe blocks and after that run the tests.

After some researching, I found path.FunSpec class that preserves ordering of each describe block, but it doesn’t allow use traits like BeforeAndAfter because of overriding runTest() method as final.

I would like to know some good practices to organise such tests with minimum code duplication. And some recommendations about my particular case.


Solution

  • By default, scalatest runs the tests in parallel to make the test time shorter.

    The fact that you have this problem points to another problem you have, and have fortunately stumbled upon - your tests are not isolated.

    To solve the problem, have each test create its own version of the mocked object. If you want to reduce code duplication, scalatest has hooks that can run code before every test.