Sharing fixtures
A test fixture is objects or other artifacts (such as files, sockets, database
connections, etc.) used by tests to do their work.
If a fixture is used by only one test method, then the definitions of the fixture objects can
be local to the method, such as the objects assigned to sum and diff in the
previous ExampleSpec examples. If multiple methods need to share an immutable fixture, one approach
is to assign them to instance variables.
In some cases, however, shared mutable fixture objects may be changed by test methods such that
they need to be recreated or reinitialized before each test. Shared resources such
as files or database connections may also need to
be created and initialized before, and cleaned up after, each test. JUnit 3 offered methods setUp and
tearDown for this purpose. In ScalaTest, you can use the BeforeAndAfterEach trait,
which will be described later, to implement an approach similar to JUnit's setUp
and tearDown, however, this approach usually involves reassigning vars or mutating objects
between tests. Before going that route, you may wish to consider some more functional approaches that
avoid side effects.
Note: Although the techniques described on this page are shown with FlatSpec examples, the techniques can be used with any style trait.
The Scaladoc for each style trait gives corresponding examples for the other style traits.
Calling create-fixture methods
One approach is to write one or more create-fixture methods
that return a new instance of a needed fixture object (or an holder object containing multiple needed fixture objects) each time it
is called. You can then call a create-fixture method at the beginning of each
test method that needs the fixture, storing the returned object or objects in local variables. Here's an example:
import org.scalatest.FlatSpec
import collection.mutable.ListBuffer
class ExampleSpec extends FlatSpec {
def fixture =
new {
val builder = new StringBuilder("ScalaTest is ")
val buffer = new ListBuffer[String]
}
"Testing" should "be easy" in {
val f = fixture
f.builder.append("easy!")
assert(f.builder.toString === "ScalaTest is easy!")
assert(f.buffer.isEmpty)
f.buffer += "sweet"
}
it should "be fun" in {
val f = fixture
f.builder.append("fun!")
assert(f.builder.toString === "ScalaTest is fun!")
assert(f.buffer.isEmpty)
}
}
The “f.” in front of each use of a fixture object provides a visual indication of which objects
are part of the fixture, but if you prefer, you can import the the members with “import f._” and use the names directly.
Instantiating fixture traits
A related technique is to place
the fixture objects in a fixture trait and run your test code in the context of a new anonymous class instance that mixes in
the fixture trait, like this:
import org.scalatest.FlatSpec
import collection.mutable.ListBuffer
class ExampleSpec extends FlatSpec {
trait Fixture {
val builder = new StringBuilder("ScalaTest is ")
val buffer = new ListBuffer[String]
}
"Testing" should "be easy" in {
new Fixture {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
}
it should "be fun" in {
new Fixture {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
}
}
}
Mixing in OneInstancePerTest
If every test method requires the same set of
mutable fixture objects, one other approach you can take is make them simply vals and mix in trait
OneInstancePerTest. If you mix in OneInstancePerTest, each test
will be run in its own instance of the Suite, similar to the way JUnit tests are executed. Here's an example:
import org.scalatest.FlatSpec
import org.scalatest.OneInstancePerTest
import collection.mutable.ListBuffer
class ExampleSpec extends FlatSpec with OneInstancePerTest {
val builder = new StringBuilder("ScalaTest is ")
val buffer = new ListBuffer[String]
"Testing" should "be easy" in {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
it should "be fun" in {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
}
}
Although the create-fixture, fixture-trait, and OneInstancePerTest approaches take care of setting up a fixture before each
test, they don't address the problem of cleaning up a fixture after the test completes. In this situation, you'll need to either
use side effects or the loan pattern.
Mixing in BeforeAndAfter
One way to use side effects is to mix in the BeforeAndAfter trait.
With this trait you can denote a bit of code to run before each test with before and/or after each test
each test with after, like this:
import org.scalatest.FlatSpec
import org.scalatest.BeforeAndAfter
import collection.mutable.ListBuffer
class ExampleSpec extends FlatSpec with BeforeAndAfter {
val builder = new StringBuilder
val buffer = new ListBuffer[String]
before {
builder.append("ScalaTest is ")
}
after {
builder.clear()
buffer.clear()
}
"Testing" should "be easy" in {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
it should "be fun" in {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
}
}
Overriding withFixture(NoArgTest)
An alternate way to take care of setup and cleanup via side effects
is to override withFixture. Trait Suite's implementation of
runTest, which is inherited by this trait, passes a no-arg test function to withFixture. It is withFixture's
responsibility to invoke that test function. Suite's implementation of withFixture simply
invokes the function, like this:
protected def withFixture(test: NoArgTest) {
test()
}
You can, therefore, override withFixture to perform setup before, and cleanup after, invoking the test function. If
you have cleanup to perform, you should invoke the test function
inside a try block and perform the cleanup in a finally clause.
Here's an example:
import org.scalatest.FlatSpec
import collection.mutable.ListBuffer
class ExampleSpec extends FlatSpec {
val builder = new StringBuilder
val buffer = new ListBuffer[String]
override def withFixture(test: NoArgTest) {
builder.append("ScalaTest is ")
try {
test()
}
finally {
builder.clear()
buffer.clear()
}
}
"Testing" should "be easy" in {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
it should "be fun" in {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
buffer += "clear"
}
}
Note that the NoArgTest passed to withFixture, in addition to
an apply method that executes the test, also includes the test name as well as the config
map passed to runTest. Thus you can also use the test name and configuration objects in withFixture.
The reason you should perform cleanup in a finally clause is that withFixture is called by
runTest, which expects an exception to be thrown to indicate a failed test. Thus when you invoke
the test function inside withFixture, it may complete abruptly with an exception. The finally
clause will ensure the fixture cleanup happens as that exception propagates back up the call stack to runTest.
Overriding withFixture(OneArgTest)
To use the loan pattern, you can extend fixture.FlatSpec (from the org.scalatest.fixture package) instead of
FlatSpec. Each test in a fixture.FlatSpec takes a fixture as a parameter, allowing you to pass the fixture into
the test. You must indicate the type of the fixture parameter by specifying FixtureParam, and implement a
withFixture method that takes a OneArgTest. This withFixture method is responsible for
invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing
the fixture into the test function. Here's an example:
import org.scalatest.fixture
import java.io.FileWriter
import java.io.File
class ExampleSpec extends fixture.FlatSpec {
final val tmpFile = "temp.txt"
type FixtureParam = FileWriter
def withFixture(test: OneArgTest) {
val writer = new FileWriter(tmpFile)
try {
test(writer)
}
finally {
writer.close()
}
}
"Testing" should "be easy" in { writer =>
writer.write("Hello, test!")
writer.flush()
assert(new File(tmpFile).length === 12)
}
it should "be fun" in { writer =>
writer.write("Hi, test!")
writer.flush()
assert(new File(tmpFile).length === 9)
}
}
For more information, see the documentation for fixture.FlatSpec.
Providing different fixtures to different tests
If different tests in the same FlatSpec require different fixtures, you can combine the previous techniques and
provide each test with just the fixture or fixtures it needs. Here's an example in which a StringBuilder and a
ListBuffer are provided via fixture traits, and file writer (that requires cleanup) is provided via the loan pattern:
import java.io.FileWriter
import java.io.File
import collection.mutable.ListBuffer
import org.scalatest.FlatSpec
class ExampleSpec extends FlatSpec {
final val tmpFile = "temp.txt"
trait Builder {
val builder = new StringBuilder("ScalaTest is ")
}
trait Buffer {
val buffer = ListBuffer("ScalaTest", "is")
}
def withWriter(testCode: FileWriter => Any) {
val writer = new FileWriter(tmpFile)
try {
testCode(writer)
}
finally {
writer.close()
}
}
"Testing" should "be productive" in {
new Builder {
builder.append("productive!")
assert(builder.toString === "ScalaTest is productive!")
}
}
it should "be readable" in {
new Buffer {
buffer += ("readable!")
assert(buffer === List("ScalaTest", "is", "readable!"))
}
}
it should "be user-friendly" in {
withWriter { writer =>
writer.write("Hello, user!")
writer.flush()
assert(new File(tmpFile).length === 12)
}
}
"Test code" should "be clear and concise" in {
new Builder with Buffer {
builder.append("clear!")
buffer += ("concise!")
assert(builder.toString === "ScalaTest is clear!")
assert(buffer === List("ScalaTest", "is", "concise!"))
}
}
it should "be composable" in {
new Builder with Buffer {
builder.append("clear!")
buffer += ("concise!")
assert(builder.toString === "ScalaTest is clear!")
assert(buffer === List("ScalaTest", "is", "concise!"))
withWriter { writer =>
writer.write(builder.toString)
writer.flush()
assert(new File(tmpFile).length === 19)
}
}
}
}
In the previous example, ScalaTest should be productive uses only the StringBuilder fixture, so it just instantiates
a new Builder, whereas it should be readable uses only the ListBuffer fixture, so it just intantiates
a new Buffer. it should be user-friendly needs just the FileWriter fixture, so it invokes
withWriter, which prepares and passes a FileWriter to the test (and takes care of closing it afterwords).
Two tests need multiple fixtures: Test code should be clear and concise needs both the StringBuilder and the
ListBuffer, so it instantiates a class that mixes in both fixture traits with new Builder with Buffer.
it should be composable needs all three fixtures, so in addition to new Builder with Buffer it also invokes
withWriter, wrapping just the of the test code that needs the fixture.
Note that in this case, the loan pattern is being implemented via the withWriter method that takes a function, not
by overriding fixture.FlatSpec's withFixture(OneArgTest) method. fixture.FlatSpec makes the most sense
if all (or at least most) tests need the same fixture, whereas in this Suite only two tests need the
FileWriter.
In the previous example, the withWriter method passed an object into
the tests. Passing fixture objects into tests is generally a good idea when possible, but sometimes a side affect is unavoidable.
For example, if you need to initialize a database running on a server across a network, your with-fixture
method will likely have nothing to pass. In such cases, simply create a with-fixture method that takes a by-name parameter and
performs setup and cleanup via side effects, like this:
def withDataInDatabase(test: => Any) {
try {
test
}
finally {
}
}
You can then use it like:
"A user" should "be able to log onto the system" in {
withDataInDatabase {
}
}
Composing stackable fixture traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations,
and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual
fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture methods in several traits, each of which call super.withFixture. Here's an example in
which the StringBuilder and ListBuffer[String] fixtures used in the previous examples have been
factored out into two stackable fixture traits named Builder and Buffer:
import org.scalatest.FlatSpec
import org.scalatest.AbstractSuite
import collection.mutable.ListBuffer
trait Builder extends AbstractSuite { this: Suite =>
val builder = new StringBuilder
abstract override def withFixture(test: NoArgTest) {
builder.append("ScalaTest is ")
try {
super.withFixture(test)
}
finally {
builder.clear()
}
}
}
trait Buffer extends AbstractSuite { this: Suite =>
val buffer = new ListBuffer[String]
abstract override def withFixture(test: NoArgTest) {
try {
super.withFixture(test)
}
finally {
buffer.clear()
}
}
}
class ExampleSpec extends FlatSpec with Builder with Buffer {
"Testing" should "be easy" in {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
it should "be fun" in {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
buffer += "clear"
}
}
By mixing in both the Builder and Buffer traits, ExampleSpec gets both fixtures, which will be
initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution.
In this case, Builder is "super" to Buffer. If you wanted Buffer to be "super"
to Builder, you need only switch the order you mix them together, like this:
class Example2Spec extends FlatSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Spec extends FlatSpec with Builder
Another way to create stackable fixture traits is by extending the BeforeAndAfterEach
and/or BeforeAndAfterAll traits.
BeforeAndAfterEach has a beforeEach method that will be run before each test (like JUnit's setUp),
and an afterEach method that will be run after (like JUnit's tearDown).
Similarly, BeforeAndAfterAll has a beforeAll method that will be run before all tests,
and an afterAll method that will be run after all tests. Here's what the previously shown example would look like if it
were rewritten to use the BeforeAndAfterEach methods instead of withFixture:
import org.scalatest.FlatSpec
import org.scalatest.BeforeAndAfterEach
import collection.mutable.ListBuffer
trait Builder extends BeforeAndAfterEach { this: Suite =>
val builder = new StringBuilder
override def beforeEach() {
builder.append("ScalaTest is ")
super.beforeEach()
}
override def afterEach() {
try {
super.afterEach()
}
finally {
builder.clear()
}
}
}
trait Buffer extends BeforeAndAfterEach { this: Suite =>
val buffer = new ListBuffer[String]
override def afterEach() {
try {
super.afterEach()
}
finally {
buffer.clear()
}
}
}
class ExampleSpec extends FlatSpec with Builder with Buffer {
"Testing" should "be easy" in {
builder.append("easy!")
assert(builder.toString === "ScalaTest is easy!")
assert(buffer.isEmpty)
buffer += "sweet"
}
it should "be fun" in {
builder.append("fun!")
assert(builder.toString === "ScalaTest is fun!")
assert(buffer.isEmpty)
buffer += "clear"
}
}
To get the same ordering as withFixture, place your super.beforeEach call at the end of each
beforeEach method, and the super.afterEach call at the beginning of each afterEach
method, as shown in the previous example. It is a good idea to invoke super.afterEach in a try
block and perform cleanup in a finally clause, as shown in the previous example, because this ensures the
cleanup code is performed even if super.afterAll throws an exception.
One difference to bear in mind between the before-and-after traits and the withFixture methods, is that if
a withFixture method completes abruptly with an exception, it is considered a failed test. By contrast, if any of the
methods on the before-and-after traits (i.e., before and after of BeforeAndAfter,
beforeEach and afterEach of BeforeAndAfterEach,
and beforeAll and afterAll of BeforeAndAfterAll) complete abruptly, it is considered a
failed suite, which will result in a SuiteAborted event.
Next, learn about sharing tests.