org.scalatest.BeforeAndAfter.scala Maven / Gradle / Ivy
/*
* Copyright 2001-2013 Artima, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.scalatest
import java.util.concurrent.atomic.AtomicReference
import exceptions.NotAllowedException
import org.scalactic.source
/**
* Trait that can be mixed into suites that need code executed before and after running each test.
*
*
* Recommended Usage:
* Use trait BeforeAndAfter
when you need to perform the same side-effects before and/or after tests, rather than at the beginning
* or end of tests. Note: For more insight into where BeforeAndAfter
fits into the big picture, see the
* Shared fixtures section in the documentation for your chosen style trait.
*
*
*
* A test fixture is composed of the objects and other artifacts (files, sockets, database
* connections, etc.) tests use to do their work.
* When multiple tests need to work with the same fixtures, it is important to try and avoid
* duplicating the fixture code across those tests. The more code duplication you have in your
* tests, the greater drag the tests will have on refactoring the actual production code.
* Trait BeforeAndAfter
offers one way to eliminate such code duplication:
* a before
clause that will register code to be run before each test,
* and an after
clause that will register code to be run after.
*
*
*
* Here's an example:
*
*
*
* package org.scalatest.examples.flatspec.beforeandafter
*
* import org.scalatest._
* 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)
* }
* }
*
*
*
* The before
and after
methods can each only be called once per Suite
,
* and cannot be invoked after run
has been invoked. If either of the registered before or after functions
* complete abruptly with an exception, it will be reported as an aborted suite and no more tests will be attempted in that suite.
*
*
*
* Note that the only way before
and after
code can communicate with test code is via some side-effecting mechanism, commonly by
* reassigning instance var
s or by changing the state of mutable objects held from instance val
s (as in this example). If using
* instance var
s or mutable objects held from instance val
s you wouldn't be able to run tests in parallel in the same instance
* of the test class unless you synchronized access to the shared, mutable state. This is why ScalaTest's ParallelTestExecution
trait extends
* OneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you
* don't need to synchronize. Were you to mix ParallelTestExecution
into the ExampleSuite
above, the tests would run in parallel just fine
* without any synchronization needed on the mutable StringBuilder
and ListBuffer[String]
objects.
*
*
*
* Although BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable
* traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you
* should use trait BeforeAndAfterEach
instead.
*
*
*
* The advantage this trait has over BeforeAndAfterEach
is that its syntax is more concise.
* The main disadvantage is that it is not stackable, whereas BeforeAndAfterEach
is. I.e.,
* you can write several traits that extend BeforeAndAfterEach
and provide beforeEach
methods
* that include a call to super.beforeEach
, and mix them together in various combinations. By contrast,
* only one call to the before
registration function is allowed in a suite or spec that mixes
* in BeforeAndAfter
. In addition, BeforeAndAfterEach
allows you to access
* the config map and test name via the TestData
passed to its beforeEach
and
* afterEach
methods, whereas BeforeAndAfter
* gives you no access to the config map.
*
*
* @author Bill Venners
*/
trait BeforeAndAfter extends SuiteMixin { this: Suite =>
private final val beforeFunctionAtomic = new AtomicReference[Option[() => Any]](None)
private final val afterFunctionAtomic = new AtomicReference[Option[() => Any]](None)
@volatile private var runHasBeenInvoked = false
/**
* Registers code to be executed before each of this suite's tests.
*
*
* This trait's implementation
* of runTest
executes the code passed to this method before running
* each test. Thus the code passed to this method can be used to set up a test fixture
* needed by each test.
*
*
* @throws NotAllowedException if invoked more than once on the same Suite
or if
* invoked after run
has been invoked on the Suite
*/
protected def before(fun: => Any)(implicit pos: source.Position): Unit = {
if (runHasBeenInvoked)
throw new NotAllowedException("You cannot call before after run has been invoked (such as, from within a test). It is probably best to move it to the top level of the Suite class so it is executed during object construction.", pos)
val success = beforeFunctionAtomic.compareAndSet(None, Some(() => fun))
if (!success)
throw new NotAllowedException("You are only allowed to call before once in each Suite that mixes in BeforeAndAfter.", pos)
}
/**
* Registers code to be executed after each of this suite's tests.
*
*
* This trait's implementation of runTest
executes the code passed to this method after running
* each test. Thus the code passed to this method can be used to tear down a test fixture
* needed by each test.
*
*
* @throws NotAllowedException if invoked more than once on the same Suite
or if
* invoked after run
has been invoked on the Suite
*/
protected def after(fun: => Any)(implicit pos: source.Position): Unit = {
if (runHasBeenInvoked)
throw new NotAllowedException("You cannot call after after run has been invoked (such as, from within a test. It is probably best to move it to the top level of the Suite class so it is executed during object construction.", pos)
val success = afterFunctionAtomic.compareAndSet(None, Some(() => fun))
if (!success)
throw new NotAllowedException("You are only allowed to call after once in each Suite that mixes in BeforeAndAfter.", pos)
}
/**
* Run a test surrounded by calls to the code passed to before
and after
, if any.
*
*
* This trait's implementation of this method ("this method") invokes
* the function registered with before
, if any,
* before running each test and the function registered with after
, if any,
* after running each test. This method runs each test by invoking super.runTest
, passing along
* the parameters passed to it.
*
*
*
* If any invocation of the function registered with before
completes abruptly with an exception, this
* method will complete abruptly with the same exception, however, before doing so, it will
* invoke the function registered with after
, if any.
* If the function registered with before
returns normally, but the subsequent call to
* super.runTest
completes abruptly with an exception, this method
* will complete abruptly with the same exception, however, before doing so, it will
* invoke the function registered with after
, if any.
* If the function registered with after
completes abruptly with an exception, this
* method will nevertheless complete abruptly with an exception previously thrown by either
* the function registered with before
or super.runTest
.
* If both the function registered with before
and super.runTest
return normally, but
* the function registered with after
completes abruptly with an exception, this method will complete
* abruptly with the exception thrown by the function registered with after
.
*
*
*
* The reason this method invokes the function registered with after
even if the function registered with before
or
* super.runTest
throws an exception is to reduce the chance that a resource
* acquired by the function registered with before
or super.runTest
prior to completing
* abruptly with the exception is not cleaned up and therefore leaked.
*
*
* @param testName the name of one test to run.
* @param args the Args
for this run
* @return a Status
object that indicates when the test started by this method has completed, and whether or not it failed .
*/
abstract protected override def runTest(testName: String, args: Args): Status = {
var thrownException: Option[Throwable] = None
val runTestStatus: Status =
try {
beforeFunctionAtomic.get match {
case Some(fun) => if (!args.runTestInNewInstance) fun()
case None =>
}
super.runTest(testName, args)
}
catch {
case e: Throwable if !Suite.anExceptionThatShouldCauseAnAbort(e) =>
thrownException = Some(e)
FailedStatus
}
// And if the exception should cause an abort, abort the after code too.
try {
val statusToReturn: Status =
if (!args.runTestInNewInstance) {
runTestStatus withAfterEffect {
try {
afterFunctionAtomic.get match {
case Some(fun) => fun()
case None =>
}
}
catch {
case ex: Throwable if !Suite.anExceptionThatShouldCauseAnAbort(ex) && thrownException.isDefined =>
// We will swallow an exception thrown from the after code if it is not test-aborting
// and an exception was already thrown by beforeEach or test itself.
}
} // Make sure that the after code is called even if (the before code or runTest) completes abruptly.
}
else
runTestStatus
thrownException match {
case Some(e) => throw e
case None =>
}
statusToReturn
}
catch {
case laterException: Exception =>
thrownException match {
// If both (the before code or runTest) and the after code throw an exception, throw the
// earlier exception and swallow the later exception. The reason we swallow
// the later exception rather than printing it is that it may be noisy because
// it is caused by the before code failing in the first place. Our goal with
// this approach is to minimize the chances that a finite non-memory resource
// acquired in before code is not cleaned up in after code.
case Some(earlierException) => throw earlierException
case None => throw laterException
}
}
}
/**
* This trait's implementation of run sets a flag indicating run has been invoked, after which
* any invocation to before
or after
will complete abruptly
* with a NotAllowedException
.
*
* @param testName an optional name of one test to run. If None
, all relevant tests should be run.
* I.e., None
acts like a wildcard that means run all relevant tests in this Suite
.
* @param args the Args
for this run
* @return a Status
object that indicates when all tests and nested suites started by this method have completed, and whether or not a failure occurred.
*/
abstract override def run(testName: Option[String], args: Args): Status = {
runHasBeenInvoked = true
super.run(testName, args)
}
}