One important feature of any test framework is setting up a test fixture and tearing it down afterwards.
In JUnit 3 for you did that by overriding the methods setup() and teardown() of the class TestCase, which every test needed to extend. JUnit 4 was a little more flexible. The name of the methods was irrelevant, instead you used annotations to tag the methods you wanted to use for setup or teardown. The anotations where are @Before, @BeforeClass, @After and @AfterClass. This lead to the somewhat messy situation where the setup of a test was sometimes called setup, sometimes Before and sometime it was given a name that actually described what it was doing like createAnEmptyStack(). As a result the setup and teardown methods weren't easily identified any more.
How does ScalaTest solve the issue? It goes back to the inheritance solution, but benefits from the possibility to inherit from multiple traits. So you can still have tests that extend some completely unrelated trait. As so often scalatest offers multiple option. For JUnit developers the following is probably the most intuitive:
import org.scalatest._
class FibonacciScalaTest extends FunSuite with BeforeAndAfterEach with BeforeAndAfterAll {
override def beforeEach() {
println("beforeEach")
}
override def afterEach() {
println("afterEach")
}
override def beforeAll() {
println("beforeAll")
}
override def afterAll() {
println("afterAll")
}
test("test something") {
println("inside the test")
assert(1 === (3 - 2))
}
test("test something else") {
println("inside the test")
assert(2 > 1)
}
}
We mix two traits BeforeAndAfterEach and BeforeAndAfterAll into our test class, which allows as to override before/afterEach and before/afterAll which get executed as probably expected yielding the following output:
beforeEach
inside the test
afterEach
beforeEach
inside the test
afterEach
afterAll
If you are using this technique to setup some variables that get used inside the test, note that there is only on instance of the test class no matter how many tests it contains. So make sure you clean everything up after your test. Since cleaning up is error prone there are other strategies you can use. You can use the OneInstancePerTest trait, which ensures that each test as its own test instance, as you are used to from JUnit.
I consider different approach more idiomatic for Scala(Test): pass the stuff you need inside your test into the test as a parameter! In order to do this for every Suite trait (like e.g. FunSuite) there is a matching Fixture* trait. Let's examine an example using the FixtureFunSuite. In order to make it work we need to do three things:
- specify what kind of fixture our tests expect. We do this by defining the type FixtureParam inside the test class
- the tests actually have to accept a fixture of that type as a parameter
- we have have to create the FixtureParam objects and pass them into the tests when calling them for execution. This happens in the method withFixture
class FibonacciScalaTest extends FixtureFunSuite {
type FixtureParam = scala.collection.mutable.Set[String]
def withFixture(testExpectingOneArgument : OneArgTest){
testExpectingOneArgument(scala.collection.mutable.Set("a","b","c"))
}
test("length increases by adding elements") { set =>
set += "x"
assert(4 === set.size)
}
test("length decreases by adding elements") { set =>
set -= "a"
assert(2 === set.size)
}
}
Note that both tests would fail if they would see the changes done by the other tests, but they don't. For just doing normal setup, this approach might be a little to verbose, but consider that you have full control over how you call the tests. You could use this in order to run each test 20 times in parallel threads in order to test for concurrency problems, or in the EDT of Swing for swing related tests. Faithful readers might remember such a list from a different article: It is exactly the kind of stuff you can do with JUnit Rules.
If you read so far you might as well be interested in Testing with Scala and More on Testing with Scalatest
Talks
Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: