yzcvxzyn
yzcvxzyn

Reputation: 11

How to initialize fixture once for all tests in scalatest?

According to documentation, fixtures can be passed to tests like this:

def withFixture(test: OneArgTest) = {
    val f = veryLengthyInitialization(test.configMap)
    withFixture(test.toNoArgTest(f))
}

describe("Some test") {
   it("should succeed") { f =>
         f.doSomething()
         // ...
    }
    // many other tests
    // ...
 }

Problem is that the initialization is run for each test, and it takes a very long time, so I would like it to run just once. Documentation suggests an alternative:

  override def beforeAll(configMap: ConfigMap) {
       val fOnce = veryLengthyInitialization(configMap)
       // but how do I pass f to the tests?
       // ugly workaround:
       f = fOnce
 }

 var f: InitType = null

 describe("Some test") {
   it("should succeed") { // hack: no parameter, use var instead
         f.doSomething()
         // ...
    }
    // many other tests
    // ...
 }

It works, but my understanding is that null should be avoided, and it's a bit silly to rely on mutation for something as basic as this. What is the correct way to write such tests?

Upvotes: 1

Views: 2275

Answers (2)

Binzi Cao
Binzi Cao

Reputation: 1085

I'm not sure if the below is something better:

trait DoSomething {
  var f:InitType  = _ 
  def doSomething = f.doSomething
}
class MyTestSpec extends DoSomething {
  override def beforeAll(configMap: ConfigMap) {
     val fOnce = veryLengthyInitialization(configMap)
     f = fOnce
  }

  it("should succeed") { 
     doSomething()
   }   
}

For any tests you need a f variable to do something, you could extend the trait and initialize it in your beforeAll methods, in addition, you could even override the doSomething in your test.

Upvotes: 0

Aldo Stracquadanio
Aldo Stracquadanio

Reputation: 6237

I don't think that there is a solution that is good in all aspects for this issue. One thing I do sometime to avoid the var is structuring the test as follows:

class SomeTest extends Spec {
  def initFixtures(): SomeFixtureType = ???
  lazy val fixtures = initFixtures()

  describe("Some test") {
    it("should succeed") in {
      fixtures.doSomething()
      // ...
    }
  }
}

In this way I don't need to use the beforeAndAfterAll thing, but you can if you want to guarantee that the fixtures are initialised before the test as follows:

override def beforeAll() { 
  super.beforeAll()
  fixtures
}

It can also be handy when dealing with asynchronous initialisation since you could have something like:

class SomeTest extends Spec {
  def initFixtures(): Future[SomeFixtureType] = ???
  lazy val fixtures = initFixtures()

  describe("Some test") {
    it("should succeed") in {
      whenReady(fixtures.flatMap { 
        f.doSomething()
        // ...
      }) { res => doSomeChecks(res) }
    }
  }
}

Having both initFixtures and fixtures is most useful in this case as you can have some tests re-initialise the system and act in isolation if they need to do effectful stuffs (e.g. use a database).

Upvotes: 1

Related Questions