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.fixture 17 18import org.scalatest._ 19import FixtureNodeFamily._ 20import scala.collection.immutable.ListSet 21import org.scalatest.StackDepthExceptionHelper.getStackDepth 22import java.util.concurrent.atomic.AtomicReference 23import java.util.ConcurrentModificationException 24import org.scalatest.events._ 25import Suite.anErrorThatShouldCauseAnAbort 26 27/** 28 * A sister trait to <code>org.scalatest.FeatureSpec</code> that can pass a fixture object into its tests. 29 * 30 * <p> 31 * The purpose of <code>FixtureFeatureSpec</code> and its subtraits is to facilitate writing tests in 32 * a functional style. Some users may prefer writing tests in a functional style in general, but one 33 * particular use case is parallel test execution (See <a href="../ParallelTestExecution.html">ParallelTestExecution</a>). To run 34 * tests in parallel, your test class must 35 * be thread safe, and a good way to make it thread safe is to make it functional. A good way to 36 * write tests that need common fixtures in a functional style is to pass the fixture objects into the tests, 37 * the style enabled by the <code>FixtureSuite</code> family of traits. 38 * </p> 39 * 40 * <p> 41 * Trait <code>FixtureFeatureSpec</code> behaves similarly to trait <code>org.scalatest.FeatureSpec</code>, except that tests may have a 42 * fixture parameter. The type of the 43 * fixture parameter is defined by the abstract <code>FixtureParam</code> type, which is declared as a member of this trait. 44 * This trait also declares an abstract <code>withFixture</code> method. This <code>withFixture</code> method 45 * takes a <code>OneArgTest</code>, which is a nested trait defined as a member of this trait. 46 * <code>OneArgTest</code> has an <code>apply</code> method that takes a <code>FixtureParam</code>. 47 * This <code>apply</code> method is responsible for running a test. 48 * This trait's <code>runTest</code> method delegates the actual running of each test to <code>withFixture</code>, passing 49 * in the test code to run via the <code>OneArgTest</code> argument. The <code>withFixture</code> method (abstract in this trait) is responsible 50 * for creating the fixture argument and passing it to the test function. 51 * </p> 52 * 53 * <p> 54 * Subclasses of this trait must, therefore, do three things differently from a plain old <code>org.scalatest.FeatureSpec</code>: 55 * </p> 56 * 57 * <ol> 58 * <li>define the type of the fixture parameter by specifying type <code>FixtureParam</code></li> 59 * <li>define the <code>withFixture(OneArgTest)</code> method</li> 60 * <li>write tests that take a fixture parameter</li> 61 * <li>(You can also define tests that don't take a fixture parameter.)</li> 62 * </ol> 63 * 64 * <p> 65 * Here's an example: 66 * </p> 67 * 68 * <pre class="stHighlight"> 69 * import org.scalatest.fixture.FixtureFeatureSpec 70 * import collection.mutable.Stack 71 * import java.util.NoSuchElementException 72 * 73 * class StackSpec extends FixtureFeatureSpec { 74 * 75 * // 1. define type FixtureParam 76 * type FixtureParam = Stack[Int] 77 * 78 * // 2. define the withFixture method 79 * def withFixture(test: OneArgTest) { 80 * val stack = new Stack[Int] 81 * stack.push(1) 82 * stack.push(2) 83 * test(stack) // "loan" the fixture to the test 84 * } 85 * 86 * feature("Pushing a value onto a stack") { 87 * 88 * // 3. write tests that take a fixture parameter 89 * scenario("User pushes a value") { stack => 90 * stack.push(9) 91 * assert(stack.size === 3) 92 * assert(stack.head === 9) 93 * } 94 * } 95 * 96 * feature("Popping a value off of a stack") { 97 * 98 * scenario("User pops a value") { stack => 99 * val top = stack.pop() 100 * assert(top === 2) 101 * assert(stack.size === 1) 102 * } 103 * 104 * // 4. You can also write tests that don't take a fixture parameter. 105 * scenario("User calls pop on an empty stack") { () => 106 * intercept[NoSuchElementException] { 107 * (new Stack[Int]).pop() 108 * } 109 * } 110 * } 111 * } 112 * </pre> 113 * 114 * <p> 115 * In the previous example, <code>withFixture</code> creates and initializes a stack, then invokes the test function, passing in 116 * the stack. In addition to setting up a fixture before a test, the <code>withFixture</code> method also allows you to 117 * clean it up afterwards, if necessary. If you need to do some clean up that must happen even if a test 118 * fails, you should invoke the test function from inside a <code>try</code> block and do the cleanup in a 119 * <code>finally</code> clause, like this: 120 * </p> 121 * 122 * <pre class="stHighlight"> 123 * def withFixture(test: OneArgTest) { 124 * val resource = someResource.open() // set up the fixture 125 * try { 126 * test(resource) // if the test fails, test(...) will throw an exception 127 * } 128 * finally { 129 * // clean up the fixture no matter whether the test succeeds or fails 130 * resource.close() 131 * } 132 * } 133 * </pre> 134 * 135 * <p> 136 * The reason you must perform cleanup in a <code>finally</code> clause is that <code>withFixture</code> is called by 137 * <code>runTest</code>, which expects an exception to be thrown to indicate a failed test. Thus when you invoke 138 * the <code>test</code> function, it may complete abruptly with an exception. The <code>finally</code> clause will 139 * ensure the fixture cleanup happens as that exception propagates back up the call stack to <code>runTest</code>. 140 * </p> 141 * 142 * <p> 143 * If the fixture you want to pass into your tests consists of multiple objects, you will need to combine 144 * them into one object to use this trait. One good approach to passing multiple fixture objects is 145 * to encapsulate them in a case class. Here's an example: 146 * </p> 147 * 148 * <pre class="stHighlight"> 149 * import org.scalatest.fixture.FixtureFeatureSpec 150 * import scala.collection.mutable.ListBuffer 151 * 152 * class ExampleSpec extends FixtureFeatureSpec { 153 * 154 * case class F(builder: StringBuilder, buffer: ListBuffer[String]) 155 * type FixtureParam = F 156 * 157 * def withFixture(test: OneArgTest) { 158 * 159 * // Create needed mutable objects 160 * val stringBuilder = new StringBuilder("ScalaTest is ") 161 * val listBuffer = new ListBuffer[String] 162 * 163 * // Invoke the test function, passing in the mutable objects 164 * test(F(stringBuilder, listBuffer)) 165 * } 166 * 167 * scenario("User finds testing easy") { f => 168 * f.builder.append("easy!") 169 * assert(f.builder.toString === "ScalaTest is easy!") 170 * assert(f.buffer.isEmpty) 171 * f.buffer += "sweet" 172 * } 173 * 174 * scenario("User finds testing fun") { f => 175 * f.builder.append("fun!") 176 * assert(f.builder.toString === "ScalaTest is fun!") 177 * assert(f.buffer.isEmpty) 178 * } 179 * } 180 * </pre> 181 * 182 * <h2>Configuring fixtures and tests</h2> 183 * 184 * <p> 185 * Sometimes you may want to write tests that are configurable. For example, you may want to write 186 * a suite of tests that each take an open temp file as a fixture, but whose file name is specified 187 * externally so that the file name can be can be changed from run to run. To accomplish this 188 * the <code>OneArgTest</code> trait has a <code>configMap</code> 189 * method, which will return a <code>Map[String, Any]</code> from which configuration information may be obtained. 190 * The <code>runTest</code> method of this trait will pass a <code>OneArgTest</code> to <code>withFixture</code> 191 * whose <code>configMap</code> method returns the <code>configMap</code> passed to <code>runTest</code>. 192 * Here's an example in which the name of a temp file is taken from the passed <code>configMap</code>: 193 * </p> 194 * 195 * <pre class="stHighlight"> 196 * import org.scalatest.fixture.FixtureFeatureSpec 197 * import java.io.FileReader 198 * import java.io.FileWriter 199 * import java.io.File 200 * 201 * class ExampleSpec extends FixtureFeatureSpec { 202 * 203 * type FixtureParam = FileReader 204 * def withFixture(test: OneArgTest) { 205 * 206 * require( 207 * test.configMap.contains("TempFileName"), 208 * "This suite requires a TempFileName to be passed in the configMap" 209 * ) 210 * 211 * // Grab the file name from the configMap 212 * val FileName = test.configMap("TempFileName").asInstanceOf[String] 213 * 214 * // Set up the temp file needed by the test 215 * val writer = new FileWriter(FileName) 216 * try { 217 * writer.write("Hello, test!") 218 * } 219 * finally { 220 * writer.close() 221 * } 222 * 223 * // Create the reader needed by the test 224 * val reader = new FileReader(FileName) 225 * 226 * try { 227 * // Run the test using the temp file 228 * test(reader) 229 * } 230 * finally { 231 * // Close and delete the temp file 232 * reader.close() 233 * val file = new File(FileName) 234 * file.delete() 235 * } 236 * } 237 * 238 * scenario("User reads the entire contents of a file") { reader => 239 * var builder = new StringBuilder 240 * var c = reader.read() 241 * while (c != -1) { 242 * builder.append(c.toChar) 243 * c = reader.read() 244 * } 245 * assert(builder.toString === "Hello, test!") 246 * } 247 * 248 * scenario("User reads just the first char of a file") { reader => 249 * assert(reader.read() === 'H') 250 * } 251 * } 252 * </pre> 253 * 254 * <p> 255 * If you want to pass into each test the entire <code>configMap</code> that was passed to <code>runTest</code>, you 256 * can mix in trait <code>ConfigMapFixture</code>. See the <a href="ConfigMapFixture.html">documentation 257 * for <code>ConfigMapFixture</code></a> for the details, but here's a quick 258 * example of how it looks: 259 * </p> 260 * 261 * <pre class="stHighlight"> 262 * import org.scalatest.fixture.FixtureFeatureSpec 263 * import org.scalatest.fixture.ConfigMapFixture 264 * 265 * class ExampleSpec extends FixtureFeatureSpec with ConfigMapFixture { 266 * 267 * feature("Test runs can be configured") { 268 * 269 * scenario("User wants to be greeted by the config map") { configMap => 270 * // Use the configMap passed to runTest in the test 271 * assert(configMap.contains("hello")) 272 * } 273 * 274 * scenario("User wants the world from the config map") { configMap => 275 * assert(configMap.contains("world")) 276 * } 277 * } 278 * } 279 * </pre> 280 * 281 * <h2>Providing multiple fixtures</h2> 282 * 283 * <p> 284 * If different tests in the same <code>FixtureFeatureSpec</code> need different shared fixtures, you can use the <em>loan pattern</em> to supply to 285 * each test just the fixture or fixtures it needs. First select the most commonly used fixture objects and pass them in via the 286 * <code>FixtureParam</code>. Then for each remaining fixture needed by multiple tests, create a <em>with<fixture name></em> 287 * method that takes a function you will use to pass the fixture to the test. Lasty, use the appropriate 288 * <em>with<fixture name></em> method or methods in each test. 289 * </p> 290 * 291 * <p> 292 * In the following example, the <code>FixtureParam</code> is set to <code>Map[String, Any]</code> by mixing in <code>ConfigMapFixture</code>. 293 * The <code>withFixture</code> method in trait <code>ConfigMapFixture</code> will pass the config map to any test that needs it. 294 * In addition, some tests in the following example need a <code>Stack[Int]</code> and others a <code>Stack[String]</code>. 295 * The <code>withIntStack</code> method takes 296 * care of supplying the <code>Stack[Int]</code> to those tests that need it, and the <code>withStringStack</code> method takes care 297 * of supplying the <code>Stack[String]</code> fixture. Here's how it looks: 298 * </p> 299 * 300 * <pre class="stHighlight"> 301 * import org.scalatest.fixture.FixtureFeatureSpec 302 * import org.scalatest.fixture.ConfigMapFixture 303 * import collection.mutable.Stack 304 * 305 * class StackSpec extends FixtureFeatureSpec with ConfigMapFixture { 306 * 307 * def withIntStack(test: Stack[Int] => Any) { 308 * val stack = new Stack[Int] 309 * stack.push(1) 310 * stack.push(2) 311 * test(stack) // "loan" the Stack[Int] fixture to the test 312 * } 313 * 314 * def withStringStack(test: Stack[String] => Any) { 315 * val stack = new Stack[String] 316 * stack.push("one") 317 * stack.push("two") 318 * test(stack) // "loan" the Stack[String] fixture to the test 319 * } 320 * 321 * scenario("User pops an Int value") { () => // This test doesn't need the configMap fixture, ... 322 * withIntStack { stack => 323 * val top = stack.pop() // But it needs the Stack[Int] fixture. 324 * assert(top === 2) 325 * assert(stack.size === 1) 326 * } 327 * } 328 * 329 * scenario("User pushes and Int value") { configMap => 330 * withIntStack { stack => 331 * val iToPush = // This test uses the configMap fixture... 332 * configMap("IntToPush").toString.toInt 333 * stack.push(iToPush) // And also uses the Stack[Int] fixture. 334 * assert(stack.size === 3) 335 * assert(stack.head === iToPush) 336 * } 337 * } 338 * 339 * scenario("User pops a String value") { () => // This test doesn't need the configMap fixture, ... 340 * withStringStack { stack => 341 * val top = stack.pop() // But it needs the Stack[String] fixture. 342 * assert(top === "two") 343 * assert(stack.size === 1) 344 * } 345 * } 346 * 347 * scenario("User pushes a String value") { configMap => 348 * withStringStack { stack => 349 * val sToPush = // This test uses the configMap fixture... 350 * configMap("StringToPush").toString 351 * stack.push(sToPush) // And also uses the Stack[Int] fixture. 352 * assert(stack.size === 3) 353 * assert(stack.head === sToPush) 354 * } 355 * } 356 * } 357 * </pre> 358 * 359 * <p> 360 * If you run the previous class in the Scala interpreter, you'll see: 361 * </p> 362 * 363 * <pre class="stREPL"> 364 * scala> import org.scalatest._ 365 * import org.scalatest._ 366 * 367 * scala> run(new StackSpec, configMap = Map("IntToPush" -> 9, "StringToPush" -> "nine")) 368 * <span class="stGreen">StackSpec: 369 * - User pops an Int value 370 * - User pushes an Int value 371 * - User pops a String value 372 * - User pushes a String value</span> 373 * </pre> 374 * 375 * @author Bill Venners 376 */ 377trait FixtureFeatureSpec extends FixtureSuite { thisSuite => 378 379 private final val engine = new FixtureEngine[FixtureParam]("concurrentFeatureSpecMod", "FixtureFeatureSpec") 380 import engine._ 381 382 /** 383 * Returns an <code>Informer</code> that during test execution will forward strings (and other objects) passed to its 384 * <code>apply</code> method to the current reporter. If invoked in a constructor, it 385 * will register the passed string for forwarding later during test execution. If invoked while this 386 * <code>FixtureFeatureSpec</code> is being executed, such as from inside a test function, it will forward the information to 387 * the current reporter immediately. If invoked at any other time, it will 388 * throw an exception. This method can be called safely by any thread. 389 */ 390 implicit protected def info: Informer = atomicInformer.get 391 392 /** 393 * Register a test with the given spec text, optional tags, and test function value that takes no arguments. 394 * An invocation of this method is called an “example.” 395 * 396 * This method will register the test for later execution via an invocation of one of the <code>execute</code> 397 * methods. The name of the test will be a concatenation of the text of all surrounding describers, 398 * from outside in, and the passed spec text, with one space placed between each item. (See the documenation 399 * for <code>testNames</code> for an example.) The resulting test name must not have been registered previously on 400 * this <code>Spec</code> instance. 401 * 402 * @param specText the specification text, which will be combined with the descText of any surrounding describers 403 * to form the test name 404 * @param testTags the optional list of tags for this test 405 * @param testFun the test function 406 * @throws DuplicateTestNameException if a test with the same name has been registered previously 407 * @throws TestRegistrationClosedException if invoked after <code>run</code> has been invoked on this suite 408 * @throws NullPointerException if <code>specText</code> or any passed test tag is <code>null</code> 409 */ 410 protected def scenario(specText: String, testTags: Tag*)(testFun: FixtureParam => Any) { 411 412 registerTest(Resources("scenario", specText), testFun, "scenarioCannotAppearInsideAnotherScenario", "FixtureFeatureSpec.scala", "scenario", testTags: _*) 413 } 414 415 /** 416 * Register a test to ignore, which has the given spec text, optional tags, and test function value that takes no arguments. 417 * This method will register the test for later ignoring via an invocation of one of the <code>execute</code> 418 * methods. This method exists to make it easy to ignore an existing test by changing the call to <code>it</code> 419 * to <code>ignore</code> without deleting or commenting out the actual test code. The test will not be executed, but a 420 * report will be sent that indicates the test was ignored. The name of the test will be a concatenation of the text of all surrounding describers, 421 * from outside in, and the passed spec text, with one space placed between each item. (See the documenation 422 * for <code>testNames</code> for an example.) The resulting test name must not have been registered previously on 423 * this <code>Spec</code> instance. 424 * 425 * @param specText the specification text, which will be combined with the descText of any surrounding describers 426 * to form the test name 427 * @param testTags the optional list of tags for this test 428 * @param testFun the test function 429 * @throws DuplicateTestNameException if a test with the same name has been registered previously 430 * @throws TestRegistrationClosedException if invoked after <code>run</code> has been invoked on this suite 431 * @throws NullPointerException if <code>specText</code> or any passed test tag is <code>null</code> 432 */ 433 protected def ignore(specText: String, testTags: Tag*)(testFun: FixtureParam => Any) { 434 registerIgnoredTest(Resources("scenario", specText), testFun , "ignoreCannotAppearInsideAScenario", "FixtureFeatureSpec.scala", "ignore", testTags: _*) 435 } 436 437 /** 438 * Describe a “subject” being specified and tested by the passed function value. The 439 * passed function value may contain more describers (defined with <code>describe</code>) and/or tests 440 * (defined with <code>it</code>). This trait's implementation of this method will register the 441 * description string and immediately invoke the passed function. 442 */ 443 protected def feature(description: String)(fun: => Unit) { 444 445 if (!currentBranchIsTrunk) 446 throw new NotAllowedException(Resources("cantNestFeatureClauses"), getStackDepth("FixtureFeatureSpec.scala", "feature")) 447 448 registerNestedBranch(description, None, fun, "featureCannotAppearInsideAScenario", "FixtureFeatureSpec.scala", "feature") 449 } 450 451 /** 452 * A <code>Map</code> whose keys are <code>String</code> tag names to which tests in this <code>Spec</code> belong, and values 453 * the <code>Set</code> of test names that belong to each tag. If this <code>FeatureSpec</code> contains no tags, this method returns an empty <code>Map</code>. 454 * 455 * <p> 456 * This trait's implementation returns tags that were passed as strings contained in <code>Tag</code> objects passed to 457 * methods <code>test</code> and <code>ignore</code>. 458 * </p> 459 */ 460 override def tags: Map[String, Set[String]] = atomic.get.tagsMap 461 462 /** 463 * Run a test. This trait's implementation runs the test registered with the name specified by 464 * <code>testName</code>. Each test's name is a concatenation of the text of all describers surrounding a test, 465 * from outside in, and the test's spec text, with one space placed between each item. (See the documenation 466 * for <code>testNames</code> for an example.) 467 * 468 * @param testName the name of one test to execute. 469 * @param reporter the <code>Reporter</code> to which results will be reported 470 * @param stopper the <code>Stopper</code> that will be consulted to determine whether to stop execution early. 471 * @param configMap a <code>Map</code> of properties that can be used by this <code>Spec</code>'s executing tests. 472 * @throws NullPointerException if any of <code>testName</code>, <code>reporter</code>, <code>stopper</code>, or <code>configMap</code> 473 * is <code>null</code>. 474 */ 475 protected override def runTest(testName: String, reporter: Reporter, stopper: Stopper, configMap: Map[String, Any], tracker: Tracker) { 476 477 478 def invokeWithFixture(theTest: TestLeaf) { 479 theTest.testFun match { 480 case wrapper: NoArgTestWrapper[_] => 481 withFixture(new FixturelessTestFunAndConfigMap(testName, wrapper.test, configMap)) 482 case fun => withFixture(new TestFunAndConfigMap(testName, fun, configMap)) 483 } 484 } 485 486 runTestImpl(thisSuite, testName, reporter, stopper, configMap, tracker, false, invokeWithFixture) 487 } 488 489 /** 490 * <p> 491 * Run zero to many of this <code>Spec</code>'s tests. 492 * </p> 493 * 494 * <p> 495 * This method takes a <code>testName</code> parameter that optionally specifies a test to invoke. 496 * If <code>testName</code> is <code>Some</code>, this trait's implementation of this method 497 * invokes <code>runTest</code> on this object, passing in: 498 * </p> 499 * 500 * <ul> 501 * <li><code>testName</code> - the <code>String</code> value of the <code>testName</code> <code>Option</code> passed 502 * to this method</li> 503 * <li><code>reporter</code> - the <code>Reporter</code> passed to this method, or one that wraps and delegates to it</li> 504 * <li><code>stopper</code> - the <code>Stopper</code> passed to this method, or one that wraps and delegates to it</li> 505 * <li><code>configMap</code> - the <code>configMap</code> passed to this method, or one that wraps and delegates to it</li> 506 * </ul> 507 * 508 * <p> 509 * This method takes a <code>Set</code> of tag names that should be included (<code>tagsToInclude</code>), and a <code>Set</code> 510 * that should be excluded (<code>tagsToExclude</code>), when deciding which of this <code>Suite</code>'s tests to execute. 511 * If <code>tagsToInclude</code> is empty, all tests will be executed 512 * except those those belonging to tags listed in the <code>tagsToExclude</code> <code>Set</code>. If <code>tagsToInclude</code> is non-empty, only tests 513 * belonging to tags mentioned in <code>tagsToInclude</code>, and not mentioned in <code>tagsToExclude</code> 514 * will be executed. However, if <code>testName</code> is <code>Some</code>, <code>tagsToInclude</code> and <code>tagsToExclude</code> are essentially ignored. 515 * Only if <code>testName</code> is <code>None</code> will <code>tagsToInclude</code> and <code>tagsToExclude</code> be consulted to 516 * determine which of the tests named in the <code>testNames</code> <code>Set</code> should be run. For more information on trait tags, see the main documentation for this trait. 517 * </p> 518 * 519 * <p> 520 * If <code>testName</code> is <code>None</code>, this trait's implementation of this method 521 * invokes <code>testNames</code> on this <code>Suite</code> to get a <code>Set</code> of names of tests to potentially execute. 522 * (A <code>testNames</code> value of <code>None</code> essentially acts as a wildcard that means all tests in 523 * this <code>Suite</code> that are selected by <code>tagsToInclude</code> and <code>tagsToExclude</code> should be executed.) 524 * For each test in the <code>testName</code> <code>Set</code>, in the order 525 * they appear in the iterator obtained by invoking the <code>elements</code> method on the <code>Set</code>, this trait's implementation 526 * of this method checks whether the test should be run based on the <code>tagsToInclude</code> and <code>tagsToExclude</code> <code>Set</code>s. 527 * If so, this implementation invokes <code>runTest</code>, passing in: 528 * </p> 529 * 530 * <ul> 531 * <li><code>testName</code> - the <code>String</code> name of the test to run (which will be one of the names in the <code>testNames</code> <code>Set</code>)</li> 532 * <li><code>reporter</code> - the <code>Reporter</code> passed to this method, or one that wraps and delegates to it</li> 533 * <li><code>stopper</code> - the <code>Stopper</code> passed to this method, or one that wraps and delegates to it</li> 534 * <li><code>configMap</code> - the <code>configMap</code> passed to this method, or one that wraps and delegates to it</li> 535 * </ul> 536 * 537 * @param testName an optional name of one test to execute. If <code>None</code>, all relevant tests should be executed. 538 * I.e., <code>None</code> acts like a wildcard that means execute all relevant tests in this <code>Spec</code>. 539 * @param reporter the <code>Reporter</code> to which results will be reported 540 * @param stopper the <code>Stopper</code> that will be consulted to determine whether to stop execution early. 541 * @param tagsToInclude a <code>Set</code> of <code>String</code> tag names to include in the execution of this <code>Spec</code> 542 * @param tagsToExclude a <code>Set</code> of <code>String</code> tag names to exclude in the execution of this <code>Spec</code> 543 * @param configMap a <code>Map</code> of key-value pairs that can be used by this <code>Spec</code>'s executing tests. 544 * @throws NullPointerException if any of <code>testName</code>, <code>reporter</code>, <code>stopper</code>, <code>tagsToInclude</code>, 545 * <code>tagsToExclude</code>, or <code>configMap</code> is <code>null</code>. 546 */ 547 protected override def runTests(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, 548 configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { 549 550 runTestsImpl(thisSuite, testName, reporter, stopper, filter, configMap, distributor, tracker, info, false, runTest) 551 } 552 553 /** 554 * An immutable <code>Set</code> of test names. If this <code>FixtureFeatureSpec</code> contains no tests, this method returns an 555 * empty <code>Set</code>. 556 * 557 * <p> 558 * This trait's implementation of this method will return a set that contains the names of all registered tests. The set's 559 * iterator will return those names in the order in which the tests were registered. Each test's name is composed 560 * of the concatenation of the text of each surrounding describer, in order from outside in, and the text of the 561 * example itself, with all components separated by a space. 562 * </p> 563 */ 564 //override def testNames: Set[String] = ListSet(atomic.get.testsList.map(_.testName): _*) 565 override def testNames: Set[String] = { 566 // I'm returning a ListSet here so that they tests will be run in registration order 567 ListSet(atomic.get.testNamesList.toArray: _*) 568 } 569 570 override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, 571 configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { 572 573 runImpl(thisSuite, testName, reporter, stopper, filter, configMap, distributor, tracker, super.run) 574 } 575 576 /** 577 * Registers shared scenarios. 578 * 579 * <p> 580 * This method enables the following syntax for shared scenarios in a <code>FixtureFeatureSpec</code>: 581 * </p> 582 * 583 * <pre class="stHighlight"> 584 * scenariosFor(nonEmptyStack(lastValuePushed)) 585 * </pre> 586 * 587 * <p> 588 * This method just provides syntax sugar intended to make the intent of the code clearer. 589 * Because the parameter passed to it is 590 * type <code>Unit</code>, the expression will be evaluated before being passed, which 591 * is sufficient to register the shared scenarios. For examples of shared scenarios, see the 592 * <a href="../FeatureSpec.html#SharedScenarios">Shared scenarios section</a> in the main documentation for 593 * trait <code>FeatureSpec</code>. 594 * </p> 595 */ 596 protected def scenariosFor(unit: Unit) {} 597 598 /** 599 * Implicitly converts a function that takes no parameters and results in <code>PendingNothing</code> to 600 * a function from <code>FixtureParam</code> to <code>Any</code>, to enable pending tests to registered as by-name parameters 601 * by methods that require a test function that takes a <code>FixtureParam</code>. 602 * 603 * <p> 604 * This method makes it possible to write pending tests as simply <code>(pending)</code>, without needing 605 * to write <code>(fixture => pending)</code>. 606 * </p> 607 */ 608 protected implicit def convertPendingToFixtureFunction(f: => PendingNothing): FixtureParam => Any = { 609 fixture => f 610 } 611 612 // I need this implicit because the function is passed to scenario as the 2nd parameter list, and 613 // I can't overload on that. I could if I took the ScenarioWord approach, but that has possibly a worse 614 // downside of people could just say scenario("...") and nothing else. 615 /** 616 * Implicitly converts a function that takes no parameters and results in <code>Any</code> to 617 * a function from <code>FixtureParam</code> to <code>Any</code>, to enable no-arg tests to registered 618 * by methods that require a test function that takes a <code>FixtureParam</code>. 619 */ 620 protected implicit def convertNoArgToFixtureFunction(fun: () => Any): (FixtureParam => Any) = 621 new NoArgTestWrapper(fun) 622} 623 624