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
, see the * Shared tests section in the main documentation for sister * traitorg.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 traitorg.scalatest.FunSpec
, except that tests * are isolated based on their path. The purpose ofpath.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 apath.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
* *var
s, * there's one and only one way to do it in apath.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 declaredfinal
. Thus you can't override *withFixture
, because it isfinal
, or mix inBeforeAndAfter
or *BeforeAndAfterEach
, because both overriderunTest
, which isfinal
in * apath.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 apath.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 * thoseprintln
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 inorg.scalatest
. Anorg.scalatest.FunSpec
* registers tests during construction and executes them whenrun
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. Whenrun
is invoked on apath.FunSpec
, it reports the registered * results and does not run the tests again. Ifrun
is invoked a second or third time, in fact, * apath.FunSpec
will each time report the same results registered during construction. If you want * to run the tests of apath.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 ofrun
, *expectedTestsCount
,tags
, ortestNames
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 * ofpath.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 * anorg.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
* * *Informer
s in anorg.scalatest.path.FunSpec
in the same manner * as in anorg.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 * anorg.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 anorg.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, anorg.scalatest.path.FunSpec
will always execute all non-ignored tests. When *run
is invoked on apath.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 anorg.scalatest.FunSpec
only executes tests afterrun
* 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 theFilter
passed torun
) will not be executed. * In anorg.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 apath.FunSpec
. Because * in apath.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 * anorg.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 * apath.FunSpec
executes tests eagerly at construction time, registering the results of those test runs * and reporting them later whenrun
is invoked, the order of nested suites versus test runs would be * different in aorg.scalatest.path.FunSpec
than in anorg.scalatest.FunSpec
. In *org.scalatest.FunSpec
's implementation ofrun
, nested suites are executed then tests * are executed. Aorg.scalatest.path.FunSpec
with nested suites would execute these in the opposite * order: first tests then nested suites. To help makepath.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 apath.FunSpec
, you can instead wrap them all in a *Suites
orSpecs
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. ASuiteCompleted
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
* * @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 anpath.FunSpec
(TestSucceeded
, *TestFailed
, orTestPending
), 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 timerun
is invoked. * By contrast, the suite completion events fired for apath.FunSpec
represent the amount of time * it took to report the registered results. (These events are not fired bypath.FunSpec
, but instead * by the entity that invokesrun
on thepath.FunSpec
.) As a result, the total time * for running the tests of apath.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. *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 apath.FunSpec
, it * will register the passed string for forwarding later whenrun
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 theit
field, * supports test (and shared test) registration inFunSpec
s. * ** 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
*/ protected class ItWord { /** * Supports test registration. * *path.FunSpec
. ** 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
* * @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 afterorg.scalatest.path.FunSpec
. *run
has been invoked on this suite * @throws NullPointerException ifspecText
or any passed test tag isnull
*/ 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
*/ def should(behaveWord: BehaveWord) = behaveWord /** * Supports the registration of shared tests. * *org.scalatest.FunSpec
. ** 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
*/ def must(behaveWord: BehaveWord) = behaveWord } /** * Supports test (and shared test) registration inorg.scalatest.FunSpec
. *FunSpec
s. * ** 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
*/ protected val it = new ItWord /** * Class that, via an instance referenced from theit
field, see the main documentation for this trait. *they
field, * supports test (and shared test) registration inFunSpec
s. * ** 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
*/ protected class TheyWord { /** * Supports test registration. * *path.FunSpec
. ** 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
* * @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 afterorg.scalatest.path.FunSpec
. *run
has been invoked on this suite * @throws NullPointerException ifspecText
or any passed test tag isnull
*/ 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
*/ def should(behaveWord: BehaveWord) = behaveWord /** * Supports the registration of shared tests. * *org.scalatest.FunSpec
. ** 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
*/ def must(behaveWord: BehaveWord) = behaveWord } /** * Supports test (and shared test) registration inorg.scalatest.FunSpec
. *FunSpec
s. * ** 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
*/ protected val they = new TheyWord /** * Supports registration of a test to ignore. * *it
field, see the main documentation for this trait. ** For more information and examples of this method's use, see the * Ignored tests section in the main documentation for sister * trait
* * @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 afterorg.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, aTestIgnored
event will be fired. *run
has been invoked on this suite * @throws NullPointerException ifspecText
or any passed test tag isnull
*/ 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 withdescribe
) and/or tests * (defined withit
). * ** 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 *
*/ protected def describe(description: String)(fun: => Unit) { handleNestedBranch(description, None, fun, "describeCannotAppearInsideAnIt", "FunSpec.scala", "describe", 4, -2, None) } /** * Supports shared test registration inorg.scalatest.path.FunSpec
. *path.FunSpec
s. * ** This field supports syntax such as the following: *
* ** it should behave like nonFullStack(stackWithOneItem) * ^ ** ** For more information and examples of the use of
behave 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 immutableSet
of test names. If thisFunSpec
contains no tests, this method returns an * emptySet
. * ** 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 thisFunSpec
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 thispath.FunSpec
'srun
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 passedFilter
. ** 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 aFilter
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 * fortestNames
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 theReporter
to which results will be reported * @param stopper theStopper
that will be consulted to determine whether to stop execution early. * @param configMap aMap
of properties that can be used by thisFreeSpec
's executing tests. * @throws NullPointerException if any oftestName
,reporter
,stopper
, orconfigMap
* isnull
. */ final protected override def runTest(testName: String, args: Args): Status = { ensureTestResultsRegistered(thisSuite) def dontInvokeWithFixture(theTest: TestLeaf) { theTest.testFun() } runTestImpl(thisSuite, testName, args, true, dontInvokeWithFixture) } /** * AMap
whose keys areString
tag names to which tests in thispath.FreeSpec
* belong, and values theSet
of test names that belong to each tag. If thispath.FreeSpec
* contains no tags, this method returns an emptyMap
. * ** 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 methodsit
andignore
. ** 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 thispath.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
isNone
, this trait's implementation of this method * will report the registered results for all tests except any excluded by the passedFilter
. * IftestName
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 callrunNestedSuites
. ** 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. IfNone
, all relevant tests should be run. * I.e.,None
acts like a wildcard that means run all relevant tests in thisSuite
. * @param args theArgs
for this run * *@throws NullPointerException if any passed parameter isnull
. * @throws IllegalArgumentException iftestName
is defined, but no test with the specified test name * exists in thisSuite
*/ 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 * apath.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 aorg.scalatest.path.FunSpec
than in anorg.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 apath.FunSpec
, you can * instead wrap them all in aSuites
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 * apath.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 aorg.scalatest.path.FunSpec
than in anorg.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 apath.FunSpec
, you can * instead wrap them all in aSuites
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) } }