org.scalatest.BeforeAndAfterAllFunctions.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.8.0 Show documentation
/* * Copyright 2001-2009 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 executing the * suite. This trait allows code to be executed before and/or after all the tests and nested suites of a * suite are run. This trait overridesFunSuite: * *run(the mainrunmethod that * takes seven parameters, an optional test name, reporter, stopper, filter, configMap, optional distributor, * and tracker) and calls the code passed to the *beforeAllmethod, if any, then callssuper.run. After thesuper.run* invocation completes, whether it returns normally or completes abruptly with an exception, * this trait'srunmethod will execute the code passed toafterAll, if any. * ** For example, the following
* *MasterSuitemixes inBeforeAndAfterAllFunctionsand * in the code passed tobeforeAll, creates and writes to a temp file. * After all of the nested suites have executed, the code passed toafterAllis invoked, which * deletes the file: ** import org.scalatest.SuperSuite * import org.scalatest.BeforeAndAfterAllFunctions * import java.io.FileReader * import java.io.FileWriter * import java.io.File * * class MasterSuite extends Suite with BeforeAndAfterAllFunctions { * * private val tempFileName = "temp.txt" * * // Set up the temp file needed by the test * beforeAll { * * val fileName = configMap(tempFileName) * * val writer = new FileWriter(fileName) * try { * writer.write("Hello, suite of tests!") * } * finally { * writer.close() * } * } * * override def nestedSuites = * List(new OneSuite, new TwoSuite, new RedSuite, new BlueSuite) * * // Delete the temp file * afterAll { * val fileName = configMap(tempFileName)) * val file = new File(fileName) * file.delete() * } * } ** ** Because the
BeforeAndAfterAllFunctionstrait invokessuper.runto run the suite, you may need to * mix this trait in last to get the desired behavior. For example, this won't * work, becauseBeforeAndAfterAllFunctionsis "super" to* class MySuite extends BeforeAndAfterAllFunctions with FunSuite *** You'd need to turn it around, so that
*FunSuiteis "super" toBeforeAndAfterAllFunctions, like this: ** class MySuite extends FunSuite with BeforeAndAfterAllFunctions ** * Note: This trait does not currently ensure that the code passed toafterAllis executed after * all the tests and nested suites are executed if aDistributoris passed. The * plan is to do that eventually, but in the meantime, be aware that the code passed toafterAllis * guaranteed to be run after all the tests and nested suites only when they are run * sequentially. * * @author Bill Venners */ trait BeforeAndAfterAllFunctions 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 any of this suite's tests or nested suites are run. * ** This trait's implementation * of
* * @throws NotAllowedException if invoked more than once on the samerunTestexecutes the code passed to this method before running * any tests or nested suites. Thus the code passed to this method can be used to set up a test fixture * needed by the entire suite. *Suiteor if * invoked afterrunhas been invoked on theSuite*/ protected def beforeAll(fun: => Any) { if (runHasBeenInvoked) throw new NotAllowedException("You cannot call beforeEach 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 beforeEach once in each Suite that mixes in BeforeAndAfterEachFunctions.", 0) } protected def afterAll() = () /** * Registers code to be executed after all of this suite's tests and nested suites have * been run. * ** This trait's implementation of
* * @throws NotAllowedException if invoked more than once on the samerunTestexecutes 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 the entire suite. *Suiteor if * invoked afterrunhas been invoked on theSuite*/ protected def afterAll(fun: => Any) { if (runHasBeenInvoked) throw new NotAllowedException("You cannot call afterEach 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 beforeEach once in each Suite that mixes in BeforeAndAfterEachFunctions.", 0) } /** * Execute a suite surrounded by calls tobeforeAllandafterAll. * ** This trait's implementation of this method ("this method") invokes
* *beforeAll(Map[String, Any])* before executing any tests or nested suites andafterAll(Map[String, Any])* after executing all tests and nested suites. It runs the suite by invokingsuper.run, passing along * the seven parameters passed to it. ** If any invocation of
*/ abstract override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { runHasBeenInvoked = true var thrownException: Option[Throwable] = None beforeFunctionAtomic.get match { case Some(fun) => fun() case None => } try { super.run(testName, reporter, stopper, filter, configMap, distributor, tracker) } catch { case e: Exception => thrownException = Some(e) } finally { try { // Make sure that code passed to afterAll, if any, is called even if run 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 } } } } }beforeAllcompletes abruptly with an exception, this * method will complete abruptly with the same exception. If any call to *super.runcompletes abruptly with an exception, this method * will complete abruptly with the same exception, however, before doing so, it will * invokeafterAll. IfafterAll also completes abruptly with an exception, this * method will nevertheless complete abruptly with the exception previously thrown by super.run. * Ifsuper.runreturns normally, butafterAllcompletes abruptly with an * exception, this method will complete abruptly with the same exception. *