org.scalatest.BeforeAndAfter.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.11.0-M3 Show documentation
/* * Copyright 2001-2008 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 /** * Trait that can be mixed into suites that need code executed before and after * running each test. This trait facilitates a style of testing in which mutable * fixture objects held in instance variables are replaced or reinitialized before each test or * suite. Here's an example: * *
FunSuite: * ** import org.scalatest._ * import scala.collection.mutable.ListBuffer * * class MySuite extends FunSuite with BeforeAndAfter { * * // Fixtures as reassignable variables and mutable objects * var sb: StringBuilder = _ * val lb = new ListBuffer[String] * * before { * sb = new StringBuilder("ScalaTest is ") * lb.clear() * } * * def testEasy() { * sb.append("easy!") * assert(sb.toString === "ScalaTest is easy!") * assert(lb.isEmpty) * lb += "sweet" * } * * def testFun() { * sb.append("fun!") * assert(sb.toString === "ScalaTest is fun!") * assert(lb.isEmpty) * } * } ** ** Because this trait invokes
super.runTest
to * run each test, you will need to mix it in after a core suite trait to get the desired behavior. For example, this won't * compile, becauseBeforeAndAfter
is "super" to* class MySuite extends BeforeAndAfter with FunSuite *** You'd need to turn it around, so that
*FunSuite
is "super" toBeforeAndAfter
, like this: ** class MySuite extends FunSuite with BeforeAndAfter ** ** The
* *before
andafter
methods can each only be called once perSuite
, * and cannot be invoked afterrun
has been invoked. ** Note: The advantage this trait has over
* * @author Bill Venners */ trait BeforeAndAfter extends AbstractSuite { this: Suite => private val beforeFunctionAtomic = new AtomicReference[Option[() => Any]](None) private val afterFunctionAtomic = new AtomicReference[Option[() => Any]](None) @volatile private var runHasBeenInvoked = false /** * Registers code to be executed before each of this suite's tests. * *BeforeAndAfterEach
is that its syntax is more concise. * The main disadvantage is that it is not stackable, whereasBeforeAndAfterEach
is. I.e., * you can write several traits that extendBeforeAndAfterEach
and providebeforeEach
methods * that include a call tosuper.beforeEach
, and mix them together in various combinations. By contrast, * only one call to thebefore
registration function is allowed in a suite or spec that mixes * inBeforeAndAfter
. In addition,BeforeAndAfterEach
allows you to access * the config map in itsbeforeEach
andafterEach
methods, whereasBeforeAndAfter
* gives you no access to the config map. ** This trait's implementation * of
* * @throws NotAllowedException if invoked more than once on the samerunTest
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. *Suite
or if * invoked afterrun
has been invoked on theSuite
*/ protected def before(fun: => Any) { 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.", 0) 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.", 0) } /** * Registers code to be executed after each of this suite's tests. * ** This trait's implementation of
* * @throws NotAllowedException if invoked more than once on the samerunTest
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. *Suite
or if * invoked afterrun
has been invoked on theSuite
*/ protected def after(fun: => Any) { 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.", 0) 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.", 0) } /** * Run a test surrounded by calls to the code passed tobefore
andafter
, 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 withafter
, if any, * after running each test. It runs each test by invokingsuper.runTest
, passing along * the five parameters passed to it. ** If any invocation of the function registered with
*/ abstract protected override def runTest(testName: String, reporter: Reporter, stopper: Stopper, configMap: Map[String, Any], tracker: Tracker) { var thrownException: Option[Throwable] = None beforeFunctionAtomic.get match { case Some(fun) => fun() case None => } try { super.runTest(testName, reporter, stopper, configMap, tracker) } catch { case e: Exception => thrownException = Some(e) } finally { try { // Make sure that afterEach is called even if runTest completes abruptly. afterFunctionAtomic.get match { case Some(fun) => fun() case None => } thrownException match { case Some(e) => throw e case None => } } catch { case laterException: Exception => thrownException match { // If both run and afterAll throw an exception, report the test exception 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 tobefore
completes abruptly with an exception, this * method will complete abruptly with the same exception. If any 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 withafter
, if any. If the function registered withafter
* also completes abruptly with an exception, this * method will nevertheless complete abruptly with the exception previously thrown bysuper.runTest
. * Ifsuper.runTest
returns normally, but the function registered withafter
completes abruptly with an * exception, this method will complete abruptly with the exception thrown by the function registered withafter
. *before
orafter
will complete abruptly * with aNotAllowedException
. */ abstract override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { runHasBeenInvoked = true super.run(testName, reporter, stopper, filter, configMap, distributor, tracker) } }