Using BeforeAndAfter in addition to BeforeAndAfterEach

723 views
Skip to first unread message

Jonathan Schwietert

unread,
May 2, 2012, 11:26:12 AM5/2/12
to scalate...@googlegroups.com
Hello, I've recently ramped up my usage of ScalaTest and love the functionality it brings!

I've begun to build some traits that perform actions needed for specific tests and in doing that realized the need to use BeforeAndAfterEach - as it allows my traits to implement they're own local functionality without affecting other traits etc.

My question is: Will mixing in BeforeAndAfter with a test class still perform its duty in addition to any other trait mix in's which utilize BeforeAndAfterEach?

Example:
trait TestHelper with Suite with BeforeAndAfterEach {
  var helperList = List[Int]()
  override def beforeEach {
    super.beforeEach
    helperList = List(1,2,3,4,5)
  }
}

class TestClass with WordSpec with BeforeAndAfter with TestHelper {
  before {
    // do some stuff before each test
  }
  // do some testing
}

Will both beforeEach in the trait, and the before block in the class be called before each test? If so...any idea what order?

Bill Venners

unread,
May 2, 2012, 12:19:14 PM5/2/12
to scalate...@googlegroups.com
Hi Jonathan,

Good question. Both BeforeAndAfter and BeforeAndAfterEach are stackable traits that override the runTest lifecycle method. They are stackable because they call super.runTest as part of their runTest implementation. So you can stack them. The order is therefore determined by the order of linearization.

The good thing about that is the behavior is very well specified. The bad thing is that linearization can be confusing to Scala programmers. The easy way to think about it is the trait you mix in last will have its runTest invoked first, and that means it will be run farthest from the test. (I.e., the leftmost trait will be the first before to happen and the last after to happen.)

I think easiest to show with an example. Here's an example in which you have two User traits, User1 and User2, which extend BeforeAndAfterEach, and an ExampleSpec class that mixes in User1 and User2 and also BeforeAndAfter:

import org.scalatest.Suite
import org.scalatest.FunSpec
import org.scalatest.BeforeAndAfterEach
import collection.mutable.ListBuffer
import org.scalatest.BeforeAndAfter

trait User1 extends BeforeAndAfterEach { this: Suite =>

  override def beforeEach() {
    println("User1 beforeEach start")
    super.beforeEach() // To be stackable, must call super.beforeEach
    println("User1 beforeEach end")
  }

  override def afterEach() {
    println("User1 afterEach start")
    try {
      super.afterEach() // To be stackable, must call super.afterEach
    }
    finally {
      println("User1 afterEach end")
    }
  }
}

trait User2 extends BeforeAndAfterEach { this: Suite =>

  override def beforeEach() {
    println("User2 beforeEach start")
    super.beforeEach() // To be stackable, must call super.beforeEach
    println("User2 beforeEach end")
  }

  override def afterEach() {
    println("User2 afterEach start")
    try {
      super.afterEach() // To be stackable, must call super.afterEach
    }
    finally {
      println("User2 afterEach end")
    }
  }
}

class ExampleSpec extends FunSpec with User1 with User2 with BeforeAndAfter {

  before {
    println("ExampleSpec's before clause")
  }

  after {
    println("ExampleSpec's after clause")
  }

  describe("Traits in Scala") {

    it("can be stacked") {
      println("IN TEST: Traits in Scala can be stacked")
    }
    it("can be understood, hopefully") {
      println("IN TEST: Traits in Scala can be understood, hopefully")
    }
  }
}

For this " with User1 with User2 with BeforeAndAfter " ordering, you get this output (note the BeforeAndAfter clauses are executed outermost):

Mi-Novia:stacking bv$ scala -cp ../fresh18/target/jar_contents/ org.scalatest.run ExampleSpec
Run starting. Expected test count is: 2
ExampleSpec:
ExampleSpec's before clause
User2 beforeEach start
User1 beforeEach start
User1 beforeEach end
User2 beforeEach end
Traits in Scala
IN TEST: Traits in Scala can be stacked
- can be stacked
User2 afterEach start
User1 afterEach start
User1 afterEach end
User2 afterEach end
ExampleSpec's after clause
ExampleSpec's before clause
User2 beforeEach start
User1 beforeEach start
User1 beforeEach end
User2 beforeEach end
IN TEST: Traits in Scala can be understood, hopefully
User2 afterEach start
User1 afterEach start
User1 afterEach end
User2 afterEach end
ExampleSpec's after clause
- can be understood, hopefully
Run completed in 80 milliseconds.
Total number of tests run: 2
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 0, ignored 0, pending 0
All tests passed.

Now consider this class that mixes the traits in a different order "with BeforeAndAfter with User1 with User2":

class ExampleSpec2 extends FunSpec with BeforeAndAfter with User1 with User2 {

  before {
    println("ExampleSpec's before clause")
  }

  after {
    println("ExampleSpec's after clause")
  }

  describe("Traits in Scala") {

    it("can be stacked") {
      println("IN TEST: Traits in Scala can be stacked")
    }
    it("can be understood, hopefully") {
      println("IN TEST: Traits in Scala can be understood, hopefully")
    }
  }
}

Now the User2 trait, which extends in BeforeAndAfterEach is rightmost, so BeforeAndAfterEach's runTest method will get to go first.

Mi-Novia:stacking bv$ scala -cp ../fresh18/target/jar_contents/ org.scalatest.run ExampleSpec2
Run starting. Expected test count is: 2
ExampleSpec2:
User2 beforeEach start
User1 beforeEach start
User1 beforeEach end
User2 beforeEach end
ExampleSpec's before clause
Traits in Scala
IN TEST: Traits in Scala can be stacked
- can be stacked
ExampleSpec's after clause
User2 afterEach start
User1 afterEach start
User1 afterEach end
User2 afterEach end
User2 beforeEach start
User1 beforeEach start
User1 beforeEach end
User2 beforeEach end
ExampleSpec's before clause
IN TEST: Traits in Scala can be understood, hopefully
ExampleSpec's after clause
User2 afterEach start
User1 afterEach start
User1 afterEach end
User2 afterEach end
- can be understood, hopefully
Run completed in 83 milliseconds.
Total number of tests run: 2
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 0, ignored 0, pending 0
All tests passed.

You can copy the code and try it yourself in different orders.

By the way the one thing you *can't* stack is multiple traits that extend BeforeAndAfter. You won't be able to mix two of those together into the same class. The reason I disallowed that is precisely because the order of execution wouldn't be obvious. BeforeAndAfterEach has methods that call super versions of themselves, so the ordering is defined by the rules of Scala.

Thanks.

Bill

--
You received this message because you are subscribed to the Google
Groups "scalatest-users" group.
To post to this group, send email to scalate...@googlegroups.com
To unsubscribe from this group, send email to
scalatest-use...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/scalatest-users?hl=en
ScalaTest itself, and documentation, is available here:
http://www.artima.com/scalatest



--
Bill Venners
Artima, Inc.
http://www.artima.com
Reply all
Reply to author
Forward
0 new messages