All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.path.FunSpec.scala Maven / Gradle / Ivy

package org.scalatest.path

import org.scalatest.verb.BehaveWord
import scala.collection.immutable.ListSet
import org.scalatest.PathEngine.isInTargetPath
import org.scalatest._
import org.scalatest.Suite.autoTagClassAnnotations

/**
 * A sister trait to org.scalatest.FunSpec that isolates tests by running each test in its own
 * instance of the test class, and for each test, only executing the path leading to that test.
 *
 * 

* Trait path.FunSpec behaves similarly to trait org.scalatest.FunSpec, except that tests * are isolated based on their path. The purpose of path.FunSpec is to facilitate writing * specification-style tests for mutable objects in a clear, boilerpate-free way. To test mutable objects, you need to * mutate them. Using a path trait, you can make a statement in text, then implement that statement in code (including * mutating state), and nest and combine these test/code pairs in any way you wish. Each test will only see * the side effects of code that is in blocks that enclose the test. Here's an example: *

* *
 * import org.scalatest.path
 * import org.scalatest.matchers.ShouldMatchers
 * import scala.collection.mutable.ListBuffer
 *
 * class ExampleSpec extends path.FunSpec with ShouldMatchers {
 *
 *   describe("A ListBuffer") {
 *
 *     val buf = ListBuffer.empty[Int] // This implements "A ListBuffer"
 *
 *     it("should be empty when created") {
 *
 *       // This test sees:
 *       //   val buf = ListBuffer.empty[Int]
 *       // So buf is: ListBuffer()
 *
 *       buf should be ('empty)
 *     }
 *
 *     describe("when 1 is appended") {
 *
 *       buf += 1 // This implements "when 1 is appended", etc...
 *
 *       it("should contain 1") {
 *
 *         // This test sees:
 *         //   val buf = ListBuffer.empty[Int]
 *         //   buf += 1
 *         // So buf is: ListBuffer(1)
 *
 *         buf.remove(0) should equal (1)
 *         buf should be ('empty)
 *       }
 *
 *       describe("when 2 is appended") {
 *
 *         buf += 2
 *
 *         it("should contain 1 and 2") {
 *
 *           // This test sees:
 *           //   val buf = ListBuffer.empty[Int]
 *           //   buf += 1
 *           //   buf += 2
 *           // So buf is: ListBuffer(1, 2)
 *
 *           buf.remove(0) should equal (1)
 *           buf.remove(0) should equal (2)
 *           buf should be ('empty)
 *         }
 *
 *         describe("when 2 is removed") {
 *
 *           buf -= 2
 *
 *           it("should contain only 1 again") {
 *
 *             // This test sees:
 *             //   val buf = ListBuffer.empty[Int]
 *             //   buf += 1
 *             //   buf += 2
 *             //   buf -= 2
 *             // So buf is: ListBuffer(1)
 *
 *             buf.remove(0) should equal (1)
 *             buf should be ('empty)
 *           }
 *         }
 *
 *         describe("when 3 is appended") {
 *
 *           buf += 3
 *
 *           it("should contain 1, 2, and 3") {
 *
 *             // This test sees:
 *             //   val buf = ListBuffer.empty[Int]
 *             //   buf += 1
 *             //   buf += 2
 *             //   buf += 3
 *             // So buf is: ListBuffer(1, 2, 3)
 *
 *             buf.remove(0) should equal (1)
 *             buf.remove(0) should equal (2)
 *             buf.remove(0) should equal (3)
 *             buf should be ('empty)
 *           }
 *         }
 *       }
 *
 *       describe("when 88 is appended") {
 *
 *         buf += 88
 *
 *         it("should contain 1 and 88") {
 *
 *           // This test sees:
 *           //   val buf = ListBuffer.empty[Int]
 *           //   buf += 1
 *           //   buf += 88
 *           // So buf is: ListBuffer(1, 88)
 *
 *           buf.remove(0) should equal (1)
 *           buf.remove(0) should equal (88)
 *           buf should be ('empty)
 *         }
 *       }
 *     }
 *
 *     it("should have size 0 when created") {
 *
 *       // This test sees:
 *       //   val buf = ListBuffer.empty[Int]
 *       // So buf is: ListBuffer()
 *
 *       buf should have size 0
 *     }
 *   }
 * }
 * 
* *

* Note that the above class is organized by writing a bit of specification text that opens a new block followed * by, at the top of the new block, some code that "implements" or "performs" what is described in the text. This is repeated as * the mutable object (here, a ListBuffer), is prepared for the enclosed tests. For example: *

* *

 * describe("A ListBuffer") {
 *   val buf = ListBuffer.empty[Int]
 * 
* *

* Or: *

* *
 * describe("when 2 is appended") {
 *   buf += 2
 * 
* *

* Note also that although each test mutates the ListBuffer, none of the other tests observe those * side effects: *

* *

 * it("should contain 1") {
 *
 *   buf.remove(0) should equal (1)
 *   // ...
 * }
 *
 * describe("when 2 is appended") {
 *
 *   buf += 2
 *
 *   it("should contain 1 and 2") {
 *
 *     // This test does not see the buf.remove(0) from the previous test,
 *     // so the first element in the ListBuffer is again 1
 *     buf.remove(0) should equal (1)
 *     buf.remove(0) should equal (2)
 * 
* *

* This kind of isolation of tests from each other is a consequence of running each test in its own instance of the test * class, and can also be achieved by simply mixing OneInstancePerTest into a regular * org.scalatest.FunSpec. However, path.FunSpec takes isolation one step further: a test * in a path.FunSpec does not observe side effects performed outside tests in earlier blocks that do not * enclose it. Here's an example: *

* *
 * describe("when 2 is removed") {
 *
 *   buf -= 2
 *
 *   // ...
 * }
 *
 * describe("when 3 is appended") {
 *
 *   buf += 3
 *
 *   it("should contain 1, 2, and 3") {
 *
 *     // This test does not see the buf -= 2 from the earlier "when 2 is removed" block,
 *     // because that block does not enclose this test, so the second element in the
 *     // ListBuffer is still 2
 *     buf.remove(0) should equal (1)
 *     buf.remove(0) should equal (2)
 *     buf.remove(0) should equal (3)
 * 
* *

* Running the full ExampleSpec, shown above, in the Scala interpeter would give you: *

* *
 * scala> import org.scalatest._
 * import org.scalatest._
 *
 * scala> run(new ExampleSpec)
 * ExampleSpec:
 * A ListBuffer
 * - should be empty when created
 *   when 1 is appended
 *   - should contain 1
 *     when 2 is appended
 *     - should contain 1 and 2
 *       when 2 is removed
 *       - should contain only 1 again
 *       when 3 is appended
 *       - should contain 1, 2, and 3
 *     when 88 is appended
 *     - should contain 1 and 88
 * - should have size 0 when created
 * 
* *

* Note: trait path.FunSpec's approach to isolation was inspired in part by the * specsy framework, written by Esko Luontola. *

* *

Shared fixtures

* *

* A test fixture is objects or other artifacts (such as files, sockets, database * connections, etc.) used by tests to do their work. * If a fixture is used by only one test, then the definitions of the fixture objects can * be local to the method. If multiple tests need to share an immutable fixture, you can simply * assign them to instance variables. If multiple tests need to share mutable fixture objects or vars, * there's one and only one way to do it in a path.FunSpec: place the mutable objects lexically before * the test. Any mutations needed by the test must be placed lexically before and/or after the test. * As used here, "Lexically before" means that the code needs to be executed during construction of that test's * instance of the test class to reach the test (or put another way, the * code is along the "path to the test.") "Lexically after" means that the code needs to be executed to exit the * constructor after the test has been executed. *

* *

* The reason lexical placement is the one and only one way to share fixtures in a path.FunSpec is because * all of its lifecycle methods are overridden and declared final. Thus you can't override * withFixture, because it is final, or mix in BeforeAndAfter or * BeforeAndAfterEach, because both override runTest, which is final in * a path.FunSpec. In short: *

* *

*

* * * *
* In a path.FunSpec, if you need some code to execute before a test, place that code lexically before * the test. If you need some code to execute after a test, place that code lexically after the test. *
*

* *

* The reason the life cycle methods are final, by the way, is to prevent users from attempting to combine * a path.FunSpec's approach to isolation with other ways ScalaTest provides to share fixtures or * execute tests, because doing so could make the resulting test code hard to reason about. A * path.FunSpec's execution model is a bit magical, but because it executes in one and only one * way, users should be able to reason about the code. * To help you visualize how a path.FunSpec is executed, consider the following variant of * ExampleSpec that includes print statements: *

* *
 * import org.scalatest.path
 * import org.scalatest.matchers.ShouldMatchers
 * import scala.collection.mutable.ListBuffer
 *
 * class ExampleSpec extends path.FunSpec with ShouldMatchers {
 *
 *   println("Start of: ExampleSpec")
 *   describe("A ListBuffer") {
 *
 *     println("Start of: A ListBuffer")
 *     val buf = ListBuffer.empty[Int]
 *
 *     it("should be empty when created") {
 *
 *       println("In test: should be empty when created; buf is: " + buf)
 *       buf should be ('empty)
 *     }
 *
 *     describe("when 1 is appended") {
 *
 *       println("Start of: when 1 is appended")
 *       buf += 1
 *
 *       it("should contain 1") {
 *
 *         println("In test: should contain 1; buf is: " + buf)
 *         buf.remove(0) should equal (1)
 *         buf should be ('empty)
 *       }
 *
 *       describe("when 2 is appended") {
 *
 *         println("Start of: when 2 is appended")
 *         buf += 2
 *
 *         it("should contain 1 and 2") {
 *
 *           println("In test: should contain 1 and 2; buf is: " + buf)
 *           buf.remove(0) should equal (1)
 *           buf.remove(0) should equal (2)
 *           buf should be ('empty)
 *         }
 *
 *         describe("when 2 is removed") {
 *
 *           println("Start of: when 2 is removed")
 *           buf -= 2
 *
 *           it("should contain only 1 again") {
 *
 *             println("In test: should contain only 1 again; buf is: " + buf)
 *             buf.remove(0) should equal (1)
 *             buf should be ('empty)
 *           }
 *
 *           println("End of: when 2 is removed")
 *         }
 *
 *         describe("when 3 is appended") {
 *
 *           println("Start of: when 3 is appended")
 *           buf += 3
 *
 *           it("should contain 1, 2, and 3") {
 *
 *             println("In test: should contain 1, 2, and 3; buf is: " + buf)
 *             buf.remove(0) should equal (1)
 *             buf.remove(0) should equal (2)
 *             buf.remove(0) should equal (3)
 *             buf should be ('empty)
 *           }
 *           println("End of: when 3 is appended")
 *         }
 *
 *         println("End of: when 2 is appended")
 *       }
 *
 *       describe("when 88 is appended") {
 *
 *         println("Start of: when 88 is appended")
 *         buf += 88
 *
 *         it("should contain 1 and 88") {
 *
 *           println("In test: should contain 1 and 88; buf is: " + buf)
 *           buf.remove(0) should equal (1)
 *           buf.remove(0) should equal (88)
 *           buf should be ('empty)
 *         }
 *
 *         println("End of: when 88 is appended")
 *       }
 *
 *       println("End of: when 1 is appended")
 *     }
 *
 *     it("should have size 0 when created") {
 *
 *       println("In test: should have size 0 when created; buf is: " + buf)
 *       buf should have size 0
 *     }
 *
 *     println("End of: A ListBuffer")
 *   }
 *   println("End of: ExampleSpec")
 *   println()
 * }
 * 
* *

* Running the above version of ExampleSpec in the Scala interpreter will give you output similar to: *

* *
 * scala> import org.scalatest._
 * import org.scalatest._
 *
 * scala> run(new ExampleSpec)
 * ExampleSpec:
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * In test: should be empty when created; buf is: ListBuffer()
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * Start of: when 1 is appended
 * In test: should contain 1; buf is: ListBuffer(1)
 * ExampleSpec:
 * End of: when 1 is appended
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * Start of: when 1 is appended
 * Start of: when 2 is appended
 * In test: should contain 1 and 2; buf is: ListBuffer(1, 2)
 * End of: when 2 is appended
 * End of: when 1 is appended
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * Start of: when 1 is appended
 * Start of: when 2 is appended
 * Start of: when 2 is removed
 * In test: should contain only 1 again; buf is: ListBuffer(1)
 * End of: when 2 is removed
 * End of: when 2 is appended
 * End of: when 1 is appended
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * Start of: when 1 is appended
 * Start of: when 2 is appended
 * Start of: when 3 is appended
 * In test: should contain 1, 2, and 3; buf is: ListBuffer(1, 2, 3)
 * End of: when 3 is appended
 * End of: when 2 is appended
 * End of: when 1 is appended
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * Start of: when 1 is appended
 * Start of: when 88 is appended
 * In test: should contain 1 and 88; buf is: ListBuffer(1, 88)
 * End of: when 88 is appended
 * End of: when 1 is appended
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * Start of: ExampleSpec
 * Start of: A ListBuffer
 * In test: should have size 0 when created; buf is: ListBuffer()
 * End of: A ListBuffer
 * End of: ExampleSpec
 *
 * A ListBuffer
 * - should be empty when created
 *   when 1 is appended
 *   - should contain 1
 *     when 2 is appended
 *     - should contain 1 and 2
 *       when 2 is removed
 *       - should contain only 1 again
 *       when 3 is appended
 *       - should contain 1, 2, and 3
 *     when 88 is appended
 *     - should contain 1 and 88
 * - should have size 0 when created
 * 
* *

* Note that each test is executed in order of appearance in the path.FunSpec, and that only * those println statements residing in blocks that enclose the test being run are executed. Any * println statements in blocks that do not form the "path" to a test are not executed in the * instance of the class that executes that test. *

* * *

How it executes

* *

* To provide its special brand of test isolation, path.FunSpec executes quite differently from its * sister trait in org.scalatest. An org.scalatest.FunSpec * registers tests during construction and executes them when run is invoked. An * org.scalatest.path.FunSpec, by contrast, runs each test in its own instance while that * instance is being constructed. During construction, it registers not the tests to run, but the results of * running those tests. When run is invoked on a path.FunSpec, it reports the registered * results and does not run the tests again. If run is invoked a second or third time, in fact, * a path.FunSpec will each time report the same results registered during construction. If you want * to run the tests of a path.FunSpec anew, you'll need to create a new instance and invoke * run on that. *

* *

* A path.FunSpec will create one instance for each "leaf" node it contains. The main kind of leaf node is * a test, such as: *

* *
 * // One instance will be created for each test
 * it("should be empty when created") {
 *   buf should be ('empty)
 * }
 * 
* *

* However, an empty scope (a scope that contains no tests or nested scopes) is also a leaf node: *

* *
 *  // One instance will be created for each empty scope
 * describe("when 99 is added") {
 *   // A scope is "empty" and therefore a leaf node if it has no
 *   // tests or nested scopes, though it may have other code (which
 *   // will be executed in the instance created for that leaf node)
 *   buf += 99
 * }
 * 
* *

* The tests will be executed sequentially, in the order of appearance. The first test (or empty scope, * if that is first) will be executed when a class that mixes in path.FunSpec is * instantiated. Only the first test will be executed during this initial instance, and of course, only * the path to that test. Then, the first time the client uses the initial instance (by invoking one of run, * expectedTestsCount, tags, or testNames on the instance), the initial instance will, * before doing anything else, ensure that any remaining tests are executed, each in its own instance. *

* *

* To ensure that the correct path is taken in each instance, and to register its test results, the initial * path.FunSpec instance must communicate with the other instances it creates for running any subsequent * leaf nodes. It does so by setting a thread-local variable prior to creating each instance (a technique * suggested by Esko Luontola). Each instance * of path.FunSpec checks the thread-local variable. If the thread-local is not set, it knows it * is an initial instance and therefore executes every block it encounters until it discovers, and executes the * first test (or empty scope, if that's the first leaf node). It then discovers, but does not execute the next * leaf node, or discovers there are no other leaf nodes remaining to execute. It communicates the path to the next * leaf node, if any, and the result of running the test it did execute, if any, back to the initial instance. The * initial instance repeats this process until all leaf nodes have been executed and all test results registered. *

* *
*

Ignored tests

* *

* You mark a test as ignored in an org.scalatest.path.FunSpec in the same manner as in * an org.scalatest.FunSpec. Please see the Ignored tests section * in its documentation for more information. *

* *

* Note that a separate instance will be created for an ignored test, * and the path to the ignored test will be executed in that instance, but the test function itself will not * be executed. Instead, a TestIgnored event will be fired. *

* * *

Informers

* *

* You output information using Informers in an org.scalatest.path.FunSpec in the same manner * as in an org.scalatest.FunSpec. Please see the Informers * section in its documentation for more information. *

* * *

Pending tests

* *

* You mark a test as pending in an org.scalatest.path.FunSpec in the same manner as in * an org.scalatest.FunSpec. Please see the Pending tests * section in its documentation for more information. *

* *

* Note that a separate instance will be created for a pending test, * and the path to the ignored test will be executed in that instance, as well as the test function (up until it * completes abruptly with a TestPendingException). *

* * *

Tagging tests

* *

* You can place tests into groups by tagging them in an org.scalatest.path.FunSpec in the same manner * as in an org.scalatest.FunSpec. Please see the Tagging tests * section in its documentation for more information. *

* *

* Note that one difference between this trait and its sister trait * org.scalatest.FunSpec is that because tests are executed at construction time, rather than each * time run is invoked, an org.scalatest.path.FunSpec will always execute all non-ignored tests. When * run is invoked on a path.FunSpec, if some tests are excluded based on tags, the registered * results of running those tests will not be reported. (But those tests will have already run and the results * registered.) By contrast, because an org.scalatest.FunSpec only executes tests after run * has been called, and at that time the tags to include and exclude are known, only tests selected by the tags * will be executed. *

* *

* In short, in an org.scalatest.FunSpec, tests not selected by the tags to include * and exclude specified for the run (via the Filter passed to run) will not be executed. * In an org.scalatest.path.FunSpec, by contrast, all non-ignored tests will be executed, each * during the construction of its own instance, and tests not selected by the tags to include and exclude specified * for a run will not be reported. (One upshot of this is that if you have tests that you want to tag as being slow so * you can sometimes exclude them during a run, you probably don't want to put them in a path.FunSpec. Because * in a path.Freespec the slow tests will be run regardless, with only their registered results not being reported * if you exclude slow tests during a run.) *

* *

Shared tests

*

* You can factor out shared tests in an org.scalatest.path.FunSpec in the same manner as in * an org.scalatest.FunSpec. Please see the Shared tests * section in its documentation for more information. *

* *

Nested suites

* *

* Nested suites are not allowed in a path.FunSpec. Because * a path.FunSpec executes tests eagerly at construction time, registering the results of those test runs * and reporting them later when run is invoked, the order of nested suites versus test runs would be * different in a org.scalatest.path.FunSpec than in an org.scalatest.FunSpec. In * org.scalatest.FunSpec's implementation of run, nested suites are executed then tests * are executed. A org.scalatest.path.FunSpec with nested suites would execute these in the opposite * order: first tests then nested suites. To help make path.FunSpec code easier to * reason about by giving readers of one less difference to think about, nested suites are not allowed. If you want * to add nested suites to a path.FunSpec, you can instead wrap them all in a * Suites or Specs object. They will * be executed in the order of appearance (unless a Distributor is passed, in which case * they will execute in parallel). *

*

* *

Durations

*

* Many ScalaTest events include a duration that indicates how long the event being reported took to execute. For * example, a TestSucceeded event provides a duration indicating how long it took for that test * to execute. A SuiteCompleted event provides a duration indicating how long it took for that entire * suite of tests to execute. *

* *

* In the test completion events fired by a path.FunSpec (TestSucceeded, * TestFailed, or TestPending), the durations reported refer * to the time it took for the tests to run. This time is registered with the test results and reported along * with the test results each time run is invoked. * By contrast, the suite completion events fired for a path.FunSpec represent the amount of time * it took to report the registered results. (These events are not fired by path.FunSpec, but instead * by the entity that invokes run on the path.FunSpec.) As a result, the total time * for running the tests of a path.FunSpec, calculated by summing the durations of all the individual * test completion events, may be greater than the duration reported for executing the entire suite. *

* * @author Bill Venners * @author Chua Chee Seng */ @Finders(Array("org.scalatest.finders.FunSpecFinder")) trait FunSpec extends org.scalatest.Suite with OneInstancePerTest { thisSuite => private final val engine = PathEngine.getEngine() import engine._ override def newInstance: FunSpec = this.getClass.newInstance.asInstanceOf[FunSpec] /** * Returns an Informer that during test execution will forward strings (and other objects) passed to its * apply method to the current reporter. If invoked in a constructor (including within a test, since * those are invoked during construction in a path.FunSpec, it * will register the passed string for forwarding later when run is invoked. If invoked at any other * time, it will throw an exception. This method can be called safely by any thread. */ implicit protected def info: Informer = atomicInformer.get /** * Class that, via an instance referenced from the it field, * supports test (and shared test) registration in FunSpecs. * *

* This class supports syntax such as the following test registration: *

* *
   * it("should be empty")
   * ^
   * 
* *

* and the following shared test registration: *

* *
   * it should behave like nonFullStack(stackWithOneItem)
   * ^
   * 
* *

* For more information and examples, see the main documentation for path.FunSpec. *

*/ protected class ItWord { /** * Supports test registration. * *

* This trait's implementation of this method will decide whether to register the text and invoke the passed function * based on whether or not this is part of the current "test path." For the details on this process, see * the How it executes section of the main documentation for * trait org.scalatest.path.FunSpec. *

* * @param testText the test text, which will be combined with the descText of any surrounding describers * to form the test name * @param testTags the optional list of tags for this test * @param testFun the test function * @throws DuplicateTestNameException if a test with the same name has been registered previously * @throws TestRegistrationClosedException if invoked after run has been invoked on this suite * @throws NullPointerException if specText or any passed test tag is null */ def apply(testText: String, testTags: Tag*)(testFun: => Unit) { handleTest(thisSuite, testText, testFun _, "itCannotAppearInsideAnotherIt", "FunSpec.scala", "apply", 3, -2, None, testTags: _*) } /** * Supports the registration of shared tests. * *

* This method supports syntax such as the following: *

* *
     * it should behave like nonFullStack(stackWithOneItem)
     *    ^
     * 
* *

* For examples of shared tests, see the Shared tests section * in the main documentation for trait org.scalatest.FunSpec. *

*/ def should(behaveWord: BehaveWord) = behaveWord /** * Supports the registration of shared tests. * *

* This method supports syntax such as the following: *

* *
     * it must behave like nonFullStack(stackWithOneItem)
     *    ^
     * 
* *

* For examples of shared tests, see the Shared tests section * in the main documentation for trait org.scalatest.FunSpec. *

*/ def must(behaveWord: BehaveWord) = behaveWord } /** * Supports test (and shared test) registration in FunSpecs. * *

* This field supports syntax such as the following: *

* *
   * it("should be empty")
   * ^
   * 
* *
 class="stExamples"
   * it should behave like nonFullStack(stackWithOneItem)
   * ^
   * 
* *

* For more information and examples of the use of the it field, see the main documentation for this trait. *

*/ protected val it = new ItWord /** * Class that, via an instance referenced from the they field, * supports test (and shared test) registration in FunSpecs. * *

* This class supports syntax such as the following test registration: *

* *
   * they("should be empty")
   * ^
   * 
* *

* and the following shared test registration: *

* *
   * they should behave like nonFullStack(stackWithOneItem)
   * ^
   * 
* *

* For more information and examples, see the main documentation for path.FunSpec. *

*/ protected class TheyWord { /** * Supports test registration. * *

* This trait's implementation of this method will decide whether to register the text and invoke the passed function * based on whether or not this is part of the current "test path." For the details on this process, see * the How it executes section of the main documentation for * trait org.scalatest.path.FunSpec. *

* * @param testText the test text, which will be combined with the descText of any surrounding describers * to form the test name * @param testTags the optional list of tags for this test * @param testFun the test function * @throws DuplicateTestNameException if a test with the same name has been registered previously * @throws TestRegistrationClosedException if invoked after run has been invoked on this suite * @throws NullPointerException if specText or any passed test tag is null */ def apply(testText: String, testTags: Tag*)(testFun: => Unit) { handleTest(thisSuite, testText, testFun _, "theyCannotAppearInsideAnotherThey", "FunSpec.scala", "apply", 3, -2, None, testTags: _*) } /** * Supports the registration of shared tests. * *

* This method supports syntax such as the following: *

* *
     * they should behave like nonFullStack(stackWithOneItem)
     *      ^
     * 
* *

* For examples of shared tests, see the Shared tests section * in the main documentation for trait org.scalatest.FunSpec. *

*/ def should(behaveWord: BehaveWord) = behaveWord /** * Supports the registration of shared tests. * *

* This method supports syntax such as the following: *

* *
     * they must behave like nonFullStack(stackWithOneItem)
     *      ^
     * 
* *

* For examples of shared tests, see the Shared tests section * in the main documentation for trait org.scalatest.FunSpec. *

*/ def must(behaveWord: BehaveWord) = behaveWord } /** * Supports test (and shared test) registration in FunSpecs. * *

* This field supports syntax such as the following: *

* *
   * it("should be empty")
   * ^
   * 
* *
 class="stExamples"
   * it should behave like nonFullStack(stackWithOneItem)
   * ^
   * 
* *

* For more information and examples of the use of the it field, see the main documentation for this trait. *

*/ protected val they = new TheyWord /** * Supports registration of a test to ignore. * *

* For more information and examples of this method's use, see the * Ignored tests section in the main documentation for sister * trait org.scalatest.FunSpec. Note that a separate instance will be created for an ignored test, * and the path to the ignored test will be executed in that instance, but the test function itself will not * be executed. Instead, a TestIgnored event will be fired. *

* * @param testText the specification text, which will be combined with the descText of any surrounding describers * to form the test name * @param testTags the optional list of tags for this test * @param testFun the test function * @throws DuplicateTestNameException if a test with the same name has been registered previously * @throws TestRegistrationClosedException if invoked after run has been invoked on this suite * @throws NullPointerException if specText or any passed test tag is null */ protected def ignore(testText: String, testTags: Tag*)(testFun: => Unit) { // Might not actually register it. Only will register it if it is its turn. handleIgnoredTest(testText, testFun _, "ignoreCannotAppearInsideAnIt", "FunSpec.scala", "ignore", 4, -2, None, testTags: _*) } /** * Describe a “subject” being specified and tested by the passed function value. The * passed function value may contain more describers (defined with describe) and/or tests * (defined with it). * *

* This class's implementation of this method will decide whether to * register the description text and invoke the passed function * based on whether or not this is part of the current "test path." For the details on this process, see * the How it executes section of the main documentation for trait * org.scalatest.path.FunSpec. *

*/ protected def describe(description: String)(fun: => Unit) { handleNestedBranch(description, None, fun, "describeCannotAppearInsideAnIt", "FunSpec.scala", "describe", 4, -2, None) } /** * Supports shared test registration in path.FunSpecs. * *

* This field supports syntax such as the following: *

* *
   * it should behave like nonFullStack(stackWithOneItem)
   *           ^
   * 
* *

* For more information and examples of the use of behave, see the * Shared tests section in the main documentation for sister * trait org.scalatest.FunSpec. *

*/ protected val behave = new BehaveWord /** * This lifecycle method is unused by this trait, and will complete abruptly with * UnsupportedOperationException if invoked. * *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final override def withFixture(test: NoArgTest) { throw new UnsupportedOperationException } /** * An immutable Set of test names. If this FunSpec contains no tests, this method returns an * empty Set. * *

* This trait's implementation of this method will first ensure that the results of all tests, each run its its * own instance executing only the path to the test, are registered. For details on this process see the * How it executes section in the main documentation for this trait. *

* *

* This trait's implementation of this method will return a set that contains the names of all registered tests. The set's * iterator will return those names in the order in which the tests were registered. Each test's name is composed * of the concatenation of the text of each surrounding describer, in order from outside in, and the text of the * example itself, with all components separated by a space. For example, consider this FunSpec: *

* *
   * import org.scalatest.path
   *
   * class StackSpec extends path.FunSpec {
   *   describe("A Stack") {
   *     describe("when not empty") {
   *       "must allow me to pop" in {}
   *     }
   *     describe("when not full") {
   *       "must allow me to push" in {}
   *     }
   *   }
   * }
   * 
* *

* Invoking testNames on this FunSpec will yield a set that contains the following * two test name strings: *

* *
   * "A Stack when not empty must allow me to pop"
   * "A Stack when not full must allow me to push"
   * 
* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final override def testNames: Set[String] = { ensureTestResultsRegistered(thisSuite) // I'm returning a ListSet here so that they tests will be run in registration order ListSet(atomic.get.testNamesList.toArray: _*) } /** * The total number of tests that are expected to run when this path.FunSpec's run method * is invoked. * *

* This trait's implementation of this method will first ensure that the results of all tests, each run its its * own instance executing only the path to the test, are registered. For details on this process see the * How it executes section in the main documentation for this trait. *

* *

* This trait's implementation of this method returns the size of the testNames List, minus * the number of tests marked as ignored as well as any tests excluded by the passed Filter. *

* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

* * @param filter a Filter with which to filter tests to count based on their tags */ final override def expectedTestCount(filter: Filter): Int = { ensureTestResultsRegistered(thisSuite) super.expectedTestCount(filter) } /** * Runs a test. * *

* This trait's implementation of this method will first ensure that the results of all tests, each run its its * own instance executing only the path to the test, are registered. For details on this process see the * How it executes section in the main documentation for this trait. *

* *

* This trait's implementation reports the test results registered with the name specified by * testName. Each test's name is a concatenation of the text of all describers surrounding a test, * from outside in, and the test's spec text, with one space placed between each item. (See the documentation * for testNames for an example.) * *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

* * @param testName the name of one test to execute. * @param reporter the Reporter to which results will be reported * @param stopper the Stopper that will be consulted to determine whether to stop execution early. * @param configMap a Map of properties that can be used by this FreeSpec's executing tests. * @throws NullPointerException if any of testName, reporter, stopper, or configMap * is null. */ final protected override def runTest(testName: String, args: Args): Status = { ensureTestResultsRegistered(thisSuite) def dontInvokeWithFixture(theTest: TestLeaf) { theTest.testFun() } runTestImpl(thisSuite, testName, args, true, dontInvokeWithFixture) } /** * A Map whose keys are String tag names to which tests in this path.FreeSpec * belong, and values the Set of test names that belong to each tag. If this path.FreeSpec * contains no tags, this method returns an empty Map. * *

* This trait's implementation of this method will first ensure that the results of all tests, each run its its * own instance executing only the path to the test, are registered. For details on this process see the * How it executes section in the main documentation for this trait. *

* *

* This trait's implementation returns tags that were passed as strings contained in Tag objects passed * to methods it and ignore. *

* *

* In addition, this trait's implementation will also auto-tag tests with class level annotations. * For example, if you annotate @Ignore at the class level, all test methods in the class will be auto-annotated with @Ignore. *

* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final override def tags: Map[String, Set[String]] = { ensureTestResultsRegistered(thisSuite) autoTagClassAnnotations(atomic.get.tagsMap, this) } /** * Runs this path.FunSpec, reporting test results that were registered when the tests * were run, each during the construction of its own instance. * *

* This trait's implementation of this method will first ensure that the results of all tests, each run its its * own instance executing only the path to the test, are registered. For details on this process see the * How it executes section in the main documentation for this trait. *

* *

If testName is None, this trait's implementation of this method * will report the registered results for all tests except any excluded by the passed Filter. * If testName is defined, it will report the results of only that named test. Because a * path.FunSpec is not allowed to contain nested suites, this trait's implementation of * this method does not call runNestedSuites. *

* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

* * @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 * *@throws NullPointerException if any passed parameter is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ final override def run(testName: Option[String], args: Args): Status = { ensureTestResultsRegistered(thisSuite) runPathTestsImpl(thisSuite, testName, args, info, true, runTest) } /** * This lifecycle method is unused by this trait, and will complete abruptly with * UnsupportedOperationException if invoked. * *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final protected override def runTests(testName: Option[String], args: Args): Status = { throw new UnsupportedOperationException // ensureTestResultsRegistered(isAnInitialInstance, this) // runTestsImpl(thisSuite, testName, reporter, stopper, filter, configMap, distributor, tracker, info, true, runTest) } /** * This lifecycle method is unused by this trait, and is implemented to do nothing. If invoked, it will * just return immediately. * *

* Nested suites are not allowed in a path.FunSpec. Because * a path.FunSpec executes tests eagerly at construction time, registering the results of * those test runs and reporting them later, the order of nested suites versus test runs would be different * in a org.scalatest.path.FunSpec than in an org.scalatest.FunSpec. In an * org.scalatest.FunSpec, nested suites are executed then tests are executed. In an * org.scalatest.path.FunSpec it would be the opposite. To make the code easy to reason about, * therefore, this is just not allowed. If you want to add nested suites to a path.FunSpec, you can * instead wrap them all in a Suites or * Specs object and put them in whatever order * you wish. *

* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final protected override def runNestedSuites(args: Args): Status = SucceededStatus /** * Returns an empty list. * *

* This lifecycle method is unused by this trait. If invoked, it will return an empty list, because * nested suites are not allowed in a path.FunSpec. Because * a path.FunSpec executes tests eagerly at construction time, registering the results of * those test runs and reporting them later, the order of nested suites versus test runs would be different * in a org.scalatest.path.FunSpec than in an org.scalatest.FunSpec. In an * org.scalatest.FunSpec, nested suites are executed then tests are executed. In an * org.scalatest.path.FunSpec it would be the opposite. To make the code easy to reason about, * therefore, this is just not allowed. If you want to add nested suites to a path.FunSpec, you can * instead wrap them all in a Suites or * Specs object and put them in whatever order * you wish. *

* *

* This trait's implementation of this method is marked as final. For insight onto why, see the * Shared fixtures section in the main documentation for this trait. *

*/ final override def nestedSuites: collection.immutable.IndexedSeq[Suite] = Vector.empty /** * Suite style name. */ final override val styleName: String = "org.scalatest.path.FunSpec" override def testDataFor(testName: String, theConfigMap: Map[String, Any] = Map.empty): TestData = { ensureTestResultsRegistered(thisSuite) createTestDataFor(testName, theConfigMap, this) } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy