1/* 2 * Copyright 2001-2009 Artima, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.scalatest 17 18/** 19 * Trait that can be mixed into suites that need methods invoked before and after executing the 20 * suite. This trait allows code to be executed before and/or after all the tests and nested suites of a 21 * suite are run. This trait overrides <code>run</code> (the main <code>run</code> method that 22 * takes seven parameters, an optional test name, reporter, stopper, filter, configMap, optional distributor, 23 * and tracker) and calls the 24 * <code>beforeAll</code> method, then calls <code>super.run</code>. After the <code>super.run</code> 25 * invocation completes, whether it returns normally or completes abruptly with an exception, 26 * this trait's <code>run</code> method will invoke <code>afterAll</code>. 27 * 28 * <p> 29 * Trait <code>BeforeAndAfterAll</code> defines two overloaded variants each of <code>beforeAll</code> 30 * and <code>afterAll</code>, one that takes a <code>configMap</code> and another that takes no 31 * arguments. This traits implemention of the variant that takes the <code>configMap</code> 32 * simply invokes the variant that takes no parameters, which does nothing. Thus you can override 33 * whichever variant you want. If you need something from the <code>configMap</code> before 34 * all tests and nested suites are run, override <code>beforeAll(Map[String, Any])</code>. Otherwise, 35 * override <code>beforeAll()</code>. 36 * </p> 37 * 38 * <p> 39 * For example, the following <code>MasterSuite</code> mixes in <code>BeforeAndAfterAll</code> and 40 * in <code>beforeAll</code>, creates and writes to a temp file, taking the name of the temp file 41 * from the <code>configMap</code>. This same <code>configMap</code> is then passed to the <code>run</code> 42 * methods of the nested suites, <code>OneSuite</code>, <code>TwoSuite</code>, <code>RedSuite</code>, 43 * and <code>BlueSuite</code>, so those suites can access the filename and, therefore, the file's 44 * contents. After all of the nested suites have executed, <code>afterAll</code> is invoked, which 45 * again grabs the file name from the <code>configMap</code> and deletes the file: 46 * </p> 47 * 48 * <pre class="stHighlight"> 49 * import org.scalatest.SuperSuite 50 * import org.scalatest.BeforeAndAfterAll 51 * import java.io.FileReader 52 * import java.io.FileWriter 53 * import java.io.File 54 * 55 * class MasterSuite extends Suite with BeforeAndAfterAll { 56 * 57 * private val FileNameKeyInGoodies = "tempFileName" 58 * 59 * // Set up the temp file needed by the test, taking 60 * // a file name from the configMap 61 * override def beforeAll(configMap: Map[String, Any]) { 62 * 63 * require( 64 * configMap.isDefinedAt(FileNameKeyInGoodies), 65 * "must place a temp file name in the configMap under the key: " + FileNameKeyInGoodies 66 * ) 67 * 68 * val fileName = configMap(tempFileName) 69 * 70 * val writer = new FileWriter(fileName) 71 * try { 72 * writer.write("Hello, suite of tests!") 73 * } 74 * finally { 75 * writer.close() 76 * } 77 * } 78 * 79 * override def nestedSuites = 80 * List(new OneSuite, new TwoSuite, new RedSuite, new BlueSuite) 81 * 82 * // Delete the temp file 83 * override def afterAll(configMap: Map[String, Any]) { 84 * // No need to require that configMap contains the key again because it won't get 85 * // here if it didn't contain the key in beforeAll 86 * val fileName = configMap(tempFileName)) 87 * val file = new File(fileName) 88 * file.delete() 89 * } 90 * } 91 * </pre> 92 * 93 * <p> 94 * Because the <code>BeforeAndAfterAll</code> trait invokes <code>super.run</code> to run the suite, you may need to 95 * mix this trait in last to get the desired behavior. For example, this won't 96 * work, because <code>BeforeAndAfterAll</code> is "super" to </code>FunSuite</code>: 97 * </p> 98 * <pre class="stHighlight"> 99 * class MySuite extends BeforeAndAfterAll with FunSuite 100 * </pre> 101 * <p> 102 * You'd need to turn it around, so that <code>FunSuite</code> is "super" to <code>BeforeAndAfterAll</code>, like this: 103 * </p> 104 * <pre class="stHighlight"> 105 * class MySuite extends FunSuite with BeforeAndAfterAll 106 * </pre> 107 * 108 * <strong>Note: This trait does not currently ensure that the code in <code>afterAll</code> is executed after 109 * all the tests and nested suites are executed if a <code>Distributor</code> is passed. The 110 * plan is to do that eventually, but in the meantime, be aware that <code>afterAll</code> is 111 * guaranteed to be run after all the tests and nested suites only when they are run 112 * sequentially.</strong> 113 * 114 * @author Bill Venners 115 */ 116trait BeforeAndAfterAll extends AbstractSuite { 117 118 this: Suite => 119 120 /** 121 * Defines a method to be run before any of this suite's tests or nested suites are run. 122 * 123 * <p> 124 * This trait's implementation 125 * of <code>run</code> invokes the overloaded form of this method that 126 * takes a <code>configMap</code> before executing 127 * any tests or nested suites. This trait's implementation of that <code>beforeAll(Map[String, Any])</code> 128 * method simply invokes this <code>beforeAll()</code> 129 * method. Thus this method can be used to set up a test fixture 130 * needed by the entire suite, when you don't need anything from the <code>configMap</code>. 131 * This trait's implementation of this method does nothing. 132 * </p> 133 */ 134 protected def beforeAll() = () 135 136 /** 137 * Defines a method (that takes a <code>configMap</code>) to be run before any 138 * of this suite's tests or nested suites are run. 139 * 140 * <p> 141 * This trait's implementation 142 * of <code>run</code> invokes this method before executing 143 * any tests or nested suites (passing in the <code>configMap</code> passed to it), thus this 144 * method can be used to set up a test fixture 145 * needed by the entire suite. This trait's implementation of this method invokes the 146 * overloaded form of <code>beforeAll</code> that takes no <code>configMap</code>. 147 * </p> 148 */ 149 protected def beforeAll(configMap: Map[String, Any]) { 150 beforeAll() 151 } 152 153 /** 154 * Defines a method to be run after all of this suite's tests and nested suites have 155 * been run. 156 * 157 * <p> 158 * This trait's implementation 159 * of <code>run</code> invokes the overloaded form of this method that 160 * takes a <code>configMap</code> after executing 161 * all tests and nested suites. This trait's implementation of that <code>afterAll(Map[String, Any])</code> method simply invokes this 162 * <code>afterAll()</code> method. Thus this method can be used to tear down a test fixture 163 * needed by the entire suite, when you don't need anything from the <code>configMap</code>. 164 * This trait's implementation of this method does nothing. 165 * </p> 166 */ 167 protected def afterAll() = () 168 169 /** 170 * Defines a method (that takes a <code>configMap</code>) to be run after 171 * all of this suite's tests and nested suites have been run. 172 * 173 * <p> 174 * This trait's implementation 175 * of <code>run</code> invokes this method after executing all tests 176 * and nested suites (passing in the <code>configMap</code> passed to it), thus this 177 * method can be used to tear down a test fixture 178 * needed by the entire suite. This trait's implementation of this method invokes the 179 * overloaded form of <code>afterAll</code> that takes no <code>configMap</code>. 180 * </p> 181 */ 182 protected def afterAll(configMap: Map[String, Any]) { 183 afterAll() 184 } 185 186 /** 187 * Execute a suite surrounded by calls to <code>beforeAll</code> and <code>afterAll</code>. 188 * 189 * <p> 190 * This trait's implementation of this method ("this method") invokes <code>beforeAll(Map[String, Any])</code> 191 * before executing any tests or nested suites and <code>afterAll(Map[String, Any])</code> 192 * after executing all tests and nested suites. It runs the suite by invoking <code>super.run</code>, passing along 193 * the seven parameters passed to it. 194 * </p> 195 * 196 * <p> 197 * If any invocation of <code>beforeAll</code> completes abruptly with an exception, this 198 * method will complete abruptly with the same exception. If any call to 199 * <code>super.run</code> completes abruptly with an exception, this method 200 * will complete abruptly with the same exception, however, before doing so, it will 201 * invoke <code>afterAll</code>. If <cod>afterAll</code> <em>also</em> completes abruptly with an exception, this 202 * method will nevertheless complete abruptly with the exception previously thrown by <code>super.run</code>. 203 * If <code>super.run</code> returns normally, but <code>afterAll</code> completes abruptly with an 204 * exception, this method will complete abruptly with the same exception. 205 * </p> 206 */ 207 abstract override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, 208 configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { 209 var thrownException: Option[Throwable] = None 210 211 beforeAll(configMap) 212 try { 213 super.run(testName, reporter, stopper, filter, configMap, distributor, tracker) 214 } 215 catch { 216 case e: Exception => thrownException = Some(e) 217 } 218 finally { 219 try { 220 afterAll(configMap) // Make sure that afterAll is called even if run completes abruptly. 221 thrownException match { 222 case Some(e) => throw e 223 case None => 224 } 225 } 226 catch { 227 case laterException: Exception => 228 thrownException match { // If both run and afterAll throw an exception, report the test exception 229 case Some(earlierException) => throw earlierException 230 case None => throw laterException 231 } 232 } 233 } 234 } 235} 236