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

org.scalatest.Suite.scala Maven / Gradle / Ivy

Go to download

ScalaTest is a free, open-source testing toolkit for Scala and Java programmers.

The newest version!
/*
 * Copyright 2001-2008 Artima, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.scalatest

import java.awt.AWTError
import java.lang.annotation._
import java.io.Serializable
import java.lang.reflect.Constructor
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.nio.charset.CoderMalfunctionError
import javax.xml.parsers.FactoryConfigurationError
import javax.xml.transform.TransformerFactoryConfigurationError
import Suite.simpleNameForTest
import Suite.parseSimpleName
import Suite.stripDollars
import Suite.formatterForSuiteStarting
import Suite.formatterForSuiteCompleted
import Suite.formatterForSuiteAborted
import Suite.anErrorThatShouldCauseAnAbort
import Suite.getSimpleNameOfAnObjectsClass
import scala.collection.immutable.TreeSet
import org.scalatest.events._
import org.scalatest.tools.StandardOutReporter


/**
 * A suite of tests. A Suite instance encapsulates a conceptual
 * suite (i.e., a collection) of tests.
 *
 * 

* This trait provides an interface that allows suites of tests to be run. * Its implementation enables a default way of writing and executing tests. Subtraits and subclasses can * override Suite's methods to enable other ways of writing and executing tests. * This trait's default approach allows tests to be defined as methods whose name starts with "test." * This approach is easy to understand, and a good way for Scala beginners to start writing tests. * More advanced Scala programmers may prefer to mix together other Suite subtraits defined in ScalaTest, * or create their own, to write tests in the way they feel makes them most productive. Here's a quick overview * of some of the options to help you get started: *

* *

* For JUnit 3 users *

* *

* If you are using JUnit 3 (version 3.8 or earlier releases) and you want to write JUnit 3 tests in Scala, look at * AssertionsForJUnit, * ShouldMatchersForJUnit, and * JUnit3Suite. *

* *

* For JUnit 4 users *

* *

* If you are using JUnit 4 and you want to write JUnit 4 tests in Scala, look at * JUnitSuite, and * JUnitRunner. With JUnitRunner, * you can use any of the traits described here and still run your tests with JUnit 4. *

* *

* For TestNG users *

* *

* If you are using TestNG and you want to write TestNG tests in Scala, look at * TestNGSuite. *

* *

* For high-level testing *

* *

* If you want to write tests at a higher level than unit tests, such as integration tests, acceptance tests, * or functional tests, check out FeatureSpec. *

* *

* For unit testing *

* *

* If you prefer a behavior-driven development (BDD) style, in which tests are combined with text that * specifies the behavior being tested, look at * Spec, * FlatSpec, and * WordSpec. Otherwise, if you just want to write tests * and don't want to combine testing with specifying, look at * FunSuite or read on to learn how to write * tests using this base trait, Suite. *

* *

* To use this trait's approach to writing tests, simply create classes that * extend Suite and define test methods. Test methods have names of the form testX, * where X is some unique, hopefully meaningful, string. A test method must be public and * can have any result type, but the most common result type is Unit. Here's an example: *

* *
 * import org.scalatest.Suite
 *
 * class MySuite extends Suite {
 *
 *   def testAddition() {
 *     val sum = 1 + 1
 *     assert(sum === 2)
 *     assert(sum + 2 === 4)
 *   }
 *
 *   def testSubtraction() {
 *     val diff = 4 - 1
 *     assert(diff === 3)
 *     assert(diff - 2 === 1)
 *   }
 * }
 * 
* *

* You can run a Suite by invoking on it one of four overloaded execute * methods. These methods, which print test results to the * standard output, are intended to serve as a * convenient way to run tests from within the Scala interpreter. For example, * to run MySuite from within the Scala interpreter, you could write: *

* *
 * scala> (new MySuite).execute()
 * 
* *

* And you would see: *

* *
 * Test Starting - MySuite: testAddition
 * Test Succeeded - MySuite: testAddition
 * Test Starting - MySuite: testSubtraction
 * Test Succeeded - MySuite: testSubtraction
 * 
* *

* Or, to run just the testAddition method, you could write: *

* *
 * scala> (new MySuite).execute("testAddition")
 * 
* *

* And you would see: *

* *
 * Test Starting - MySuite: testAddition
 * Test Succeeded - MySuite: testAddition
 * 
* *

* Two other execute methods that are intended to be run from the interpreter accept a "config" map of key-value * pairs (see Config map, below). Each of these execute methods invokes a run method takes seven * parameters. This run method, which actually executes the suite, will usually be invoked by a test runner, such * as org.scalatest.tools.Runner or an IDE. See the documentation * for Runner for more detail. *

* *

Assertions and ===

* *

* Inside test methods in a Suite, you can write assertions by invoking assert and passing in a Boolean expression, * such as: *

* *
 * val left = 2
 * val right = 1
 * assert(left == right)
 * 
* *

* If the passed expression is true, assert will return normally. If false, * assert will complete abruptly with a TestFailedException. This exception is usually not caught * by the test method, which means the test method itself will complete abruptly by throwing the TestFailedException. Any * test method that completes abruptly with a TestFailedException or any Exception is considered a failed * test. A test method that returns normally is considered a successful test. *

* *

* If you pass a Boolean expression to assert, a failed assertion will be reported, but without * reporting the left and right values. You can alternatively encode these values in a String passed as * a second argument to assert, as in: *

* *
 * val left = 2
 * val right = 1
 * assert(left == right, left + " did not equal " + right)
 * 
* *

* Using this form of assert, the failure report will include the left and right values, thereby * helping you debug the problem. However, ScalaTest provides the === operator to make this easier. * (The === operator is defined in trait Assertions which trait Suite extends.) * You use it like this: *

* *
 * val left = 2
 * val right = 1
 * assert(left === right)
 * 
* *

* Because you use === here instead of ==, the failure report will include the left * and right values. For example, the detail message in the thrown TestFailedException from the assert * shown previously will include, "2 did not equal 1". * From this message you will know that the operand on the left had the value 2, and the operand on the right had the value 1. *

* *

* If you're familiar with JUnit, you would use === * in a ScalaTest Suite where you'd use assertEquals in a JUnit TestCase. * The === operator is made possible by an implicit conversion from Any * to Equalizer. If you're curious to understand the mechanics, see the documentation for * Equalizer and the convertToEqualizer method. *

* *

Expected results

* * Although === provides a natural, readable extension to Scala's assert mechanism, * as the operands become lengthy, the code becomes less readable. In addition, the === comparison * doesn't distinguish between actual and expected values. The operands are just called left and right, * because if one were named expected and the other actual, it would be difficult for people to * remember which was which. To help with these limitations of assertions, Suite includes a method called expect that * can be used as an alternative to assert with ===. To use expect, you place * the expected value in parentheses after expect, followed by curly braces containing code * that should result in the expected value. For example: * *
 * val a = 5
 * val b = 2
 * expect(2) {
 *   a - b
 * }
 * 
* *

* In this case, the expected value is 2, and the code being tested is a - b. This expectation will fail, and * the detail message in the TestFailedException will read, "Expected 2, but got 3." *

* *

Intercepted exceptions

* *

* Sometimes you need to test whether a method throws an expected exception under certain circumstances, such * as when invalid arguments are passed to the method. You can do this in the JUnit style, like this: *

* *
 * val s = "hi"
 * try {
 *   s.charAt(-1)
 *   fail()
 * }
 * catch {
 *   case _: IndexOutOfBoundsException => // Expected, so continue
 * }
 * 
* *

* If charAt throws IndexOutOfBoundsException as expected, control will transfer * to the catch case, which does nothing. If, however, charAt fails to throw an exception, * the next statement, fail(), will be executed. The fail method always completes abruptly with * a TestFailedException, thereby signaling a failed test. *

* *

* To make this common use case easier to express and read, ScalaTest provides an intercept * method. You use it like this: *

* *
 * val s = "hi"
 * intercept[IndexOutOfBoundsException] {
 *   s.charAt(-1)
 * }
 * 
* *

* This code behaves much like the previous example. If charAt throws an instance of IndexOutOfBoundsException, * intercept will return that exception. But if charAt completes normally, or throws a different * exception, intercept will complete abruptly with a TestFailedException. The intercept method returns the * caught exception so that you can inspect it further if you wish, for example, to ensure that data contained inside * the exception has the expected values. Here's an example: *

* *
 * val s = "hi"
 * val caught =
 *   intercept[IndexOutOfBoundsException] {
 *     s.charAt(-1)
 *   }
 * assert(caught.getMessage === "String index out of range: -1")
 * 
* *

Using other assertions

* *

* ScalaTest also supports another style of assertions via its matchers DSL. By mixing in * trait ShouldMatchers, you can * write suites that look like: *

* *
 * import org.scalatest.Suite
 * import org.scalatest.matchers.ShouldMatchers
 *
 * class MySuite extends Suite with ShouldMatchers {
 *
 *   def testAddition() {
 *     val sum = 1 + 1
 *     sum should equal (2)
 *     sum + 2 should equal (4)
 *   }
 *
 *   def testSubtraction() {
 *     val diff = 4 - 1
 *     diff should equal (3)
 *     diff - 2 should equal (1)
 *   }
 * }
 * 
* *

If you prefer the word "must" to the word "should," you can alternatively mix in * trait MustMatchers. *

* *

* If you are comfortable with assertion mechanisms from other test frameworks, chances * are you can use them with ScalaTest. Any assertion mechanism that indicates a failure with an exception * can be used as is with ScalaTest. For example, to use the assertEquals * methods provided by JUnit or TestNG, simply import them and use them. (You will of course need * to include the relevant JAR file for the framework whose assertions you want to use on either the * classpath or runpath when you run your tests.) Here's an example in which JUnit's assertions are * imported, then used within a ScalaTest suite: *

* *
 * import org.scalatest.Suite
 * import org.junit.Assert._
 *
 * class MySuite extends Suite {
 *
 *   def testAddition() {
 *     val sum = 1 + 1
 *     assertEquals(2, sum)
 *     assertEquals(4, sum + 2)
 *   }
 *
 *   def testSubtraction() {
 *     val diff = 4 - 1
 *     assertEquals(3, diff)
 *     assertEquals(1, diff - 2)
 *   }
 * }
 * 
* *

Nested suites

* *

* A Suite can refer to a collection of other Suites, * which are called nested Suites. Those nested Suites can in turn have * their own nested Suites, and so on. Large test suites can be organized, therefore, as a tree of * nested Suites. * This trait's run method, in addition to invoking its * test methods, invokes run on each of its nested Suites. *

* *

* A List of a Suite's nested Suites can be obtained by invoking its * nestedSuites method. If you wish to create a Suite that serves as a * container for nested Suites, whether or not it has test methods of its own, simply override nestedSuites * to return a List of the nested Suites. Because this is a common use case, ScalaTest provides * a convenience SuperSuite class, which takes a List of nested Suites as a constructor * parameter. Here's an example: *

* *
 * import org.scalatest.Suite
 *
 * class ASuite extends Suite
 * class BSuite extends Suite
 * class CSuite extends Suite
 *
 * class AlphabetSuite extends SuperSuite(
 *   List(
 *     new ASuite,
 *     new BSuite,
 *     new CSuite
 *   )
 * )
 * 
* *

* If you now run AlphabetSuite, for example from the interpreter: *

* *
 * scala> (new AlphabetSuite).run()
 * 
* *

* You will see reports printed to the standard output that indicate nested * suites—ASuite, BSuite, and * CSuite—were run. *

* *

* Note that Runner can discover Suites automatically, so you need not * necessarily specify SuperSuites explicitly. See the documentation * for Runner for more information. *

* *

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 method, then the definitions of the fixture objects can * be local to the method, such as the objects assigned to sum and diff in the * previous MySuite examples. If multiple methods need to share an immutable fixture, one approach * is to assign them to instance variables. Here's a (very contrived) example, in which the object assigned * to shared is used by multiple test methods: *

* *
 * import org.scalatest.Suite
 *
 * class MySuite extends Suite {
 *
 *   // Sharing immutable fixture objects via instance variables
 *   val shared = 5
 *
 *   def testAddition() {
 *     val sum = 2 + 3
 *     assert(sum === shared)
 *   }
 *
 *   def testSubtraction() {
 *     val diff = 7 - 2
 *     assert(diff === shared)
 *   }
 * }
 * 
* *

* In some cases, however, shared mutable fixture objects may be changed by test methods such that * they need to be recreated or reinitialized before each test. Shared resources such * as files or database connections may also need to * be created and initialized before, and cleaned up after, each test. JUnit 3 offers methods setUp and * tearDown for this purpose. In ScalaTest, you can use the BeforeAndAfterEach trait, * which will be described later, to implement an approach similar to JUnit's setUp * and tearDown, however, this approach usually involves reassigning vars * between tests. Before going that route, you may wish to consider some approaches that * avoid vars. One approach is to write one or more create-fixture methods * that return a new instance of a needed object (or a tuple or case class holding new instances of * multiple objects) each time it is called. You can then call a create-fixture method at the beginning of each * test method that needs the fixture, storing the fixture object or objects in local variables. Here's an example: *

* *
 * import org.scalatest.Suite
 * import scala.collection.mutable.ListBuffer
 *
 * class MySuite extends Suite {
 *
 *   // create objects needed by tests and return as a tuple
 *   def createFixture = (
 *     new StringBuilder("ScalaTest is "),
 *     new ListBuffer[String]
 *   )
 *
 *   def testEasy() {
 *     val (builder, lbuf) = createFixture
 *     builder.append("easy!")
 *     assert(builder.toString === "ScalaTest is easy!")
 *     assert(lbuf.isEmpty)
 *     lbuf += "sweet"
 *   }
 *
 *   def testFun() {
 *     val (builder, lbuf) = createFixture
 *     builder.append("fun!")
 *     assert(builder.toString === "ScalaTest is fun!")
 *     assert(lbuf.isEmpty)
 *   }
 * }
 * 
* *

* If different tests in the same Suite require different fixtures, you can create multiple create-fixture methods and * call the method (or methods) needed by each test at the begining of the test. If every test method requires the same set of * mutable fixture objects, one other approach you can take is make them simply vals and mix in trait * OneInstancePerTest. If you mix in OneInstancePerTest, each test * will be run in its own instance of the Suite, similar to the way JUnit tests are executed. *

* *

* Although the create-fixture and OneInstancePerTest approaches take care of setting up a fixture before each * test, they don't address the problem of cleaning up a fixture after the test completes. In this situation, * one option is to mix in the BeforeAndAfterEach trait. * BeforeAndAfterEach's beforeEach method will be run before, and its afterEach * method after, each test (like JUnit's setUp and tearDown * methods, respectively). * For example, you could create a temporary file before each test, and delete it afterwords, like this: *

* *
 * import org.scalatest.Suite
 * import org.scalatest.BeforeAndAfterEach
 * import java.io.FileReader
 * import java.io.FileWriter
 * import java.io.File
 *
 * class MySuite extends Suite with BeforeAndAfterEach {
 *
 *   private val FileName = "TempFile.txt"
 *   private var reader: FileReader = _
 *
 *   // Set up the temp file needed by the test
 *   override def beforeEach() {
 *     val writer = new FileWriter(FileName)
 *     try {
 *       writer.write("Hello, test!")
 *     }
 *     finally {
 *       writer.close()
 *     }
 *
 *     // Create the reader needed by the test
 *     reader = new FileReader(FileName)
 *   }
 *
 *   // Close and delete the temp file
 *   override def afterEach() {
 *     reader.close()
 *     val file = new File(FileName)
 *     file.delete()
 *   }
 *
 *   def testReadingFromTheTempFile() {
 *     var builder = new StringBuilder
 *     var c = reader.read()
 *     while (c != -1) {
 *       builder.append(c.toChar)
 *       c = reader.read()
 *     }
 *     assert(builder.toString === "Hello, test!")
 *   }
 *
 *   def testFirstCharOfTheTempFile() {
 *     assert(reader.read() === 'H')
 *   }
 * 
 *   def testWithoutAFixture() {
 *     assert(1 + 1 === 2)
 *   }
 * }
 * 
* *

* In this example, the instance variable reader is a var, so * it can be reinitialized between tests by the beforeEach method. *

* *

* Although the BeforeAndAfterEach approach should be familiar to the users of most * test other frameworks, ScalaTest provides another alternative that also allows you to perform cleanup * after each test: overriding withFixture(NoArgTest). * To execute each test, Suite's implementation of the runTest method wraps an invocation * of the appropriate test method in a no-arg function. runTest passes that test function to the withFixture(NoArgTest) * method, which is responsible for actually running the test by invoking the function. Suite's * implementation of withFixture(NoArgTest) simply invokes the function, like this: *

* *
 * // Default implementation
 * protected def withFixture(test: NoArgTest) {
 *   test()
 * }
 * 
* *

* The withFixture(NoArgTest) method exists so that you can override it and set a fixture up before, and clean it up after, each test. * Thus, the previous temp file example could also be implemented without mixing in BeforeAndAfterEach, like this: *

* *
 * import org.scalatest.Suite
 * import java.io.FileReader
 * import java.io.FileWriter
 * import java.io.File
 *
 * class MySuite extends Suite {
 *
 *   private var reader: FileReader = _
 *
 *   override def withFixture(test: NoArgTest) {
 *
 *     val FileName = "TempFile.txt"
 *
 *     // Set up the temp file needed by the test
 *     val writer = new FileWriter(FileName)
 *     try {
 *       writer.write("Hello, test!")
 *     }
 *     finally {
 *       writer.close()
 *     }
 *
 *     // Create the reader needed by the test
 *     reader = new FileReader(FileName)
 *
 *     try {
 *       test() // Invoke the test function
 *     }
 *     finally {
 *       // Close and delete the temp file
 *       reader.close()
 *       val file = new File(FileName)
 *       file.delete()
 *     }
 *   }
 *
 *   def testReadingFromTheTempFile() {
 *     var builder = new StringBuilder
 *     var c = reader.read()
 *     while (c != -1) {
 *       builder.append(c.toChar)
 *       c = reader.read()
 *     }
 *     assert(builder.toString === "Hello, test!")
 *   }
 *
 *   def testFirstCharOfTheTempFile() {
 *     assert(reader.read() === 'H')
 *   }
 * 
 *   def testWithoutAFixture() {
 *     assert(1 + 1 === 2)
 *   }
 * }
 * 
* *

* If you prefer to keep your test classes immutable, one final variation is to use the * FixtureSuite trait from the * org.scalatest.fixture package. Tests in an org.scalatest.fixture.FixtureSuite can have a fixture * object passed in as a parameter. You must indicate the type of the fixture object * by defining the Fixture type member and define a withFixture method that takes a one-arg test function. * (A FixtureSuite has two overloaded withFixture methods, therefore, one that takes a OneArgTest * and the other, inherited from Suite, that takes a NoArgTest.) * Inside the withFixture(OneArgTest) method, you create the fixture, pass it into the test function, then perform any * necessary cleanup after the test function returns. Instead of invoking each test directly, a FixtureSuite will * pass a function that invokes the code of a test to withFixture(OneArgTest). Your withFixture(OneArgTest) method, therefore, * is responsible for actually running the code of the test by invoking the test function. * For example, you could pass the temp file reader fixture to each test that needs it * by overriding the withFixture(OneArgTest) method of a FixtureSuite, like this: *

* *
 * import org.scalatest.fixture.FixtureSuite
 * import java.io.FileReader
 * import java.io.FileWriter
 * import java.io.File
 * 
 * class MySuite extends FixtureSuite {
 *
 *   // No vars needed in this one
 *
 *   type FixtureParam = FileReader
 *
 *   def withFixture(test: OneArgTest) {
 *
 *     val FileName = "TempFile.txt"
 *
 *     // Set up the temp file needed by the test
 *     val writer = new FileWriter(FileName)
 *     try {
 *       writer.write("Hello, test!")
 *     }
 *     finally {
 *       writer.close()
 *     }
 *
 *     // Create the reader needed by the test
 *     val reader = new FileReader(FileName)
 *  
 *     try {
 *       // Run the test, passing in the temp file reader
 *       test(reader)
 *     }
 *     finally {
 *       // Close and delete the temp file
 *       reader.close()
 *       val file = new File(FileName)
 *       file.delete()
 *     }
 *   }
 * 
 *   def testReadingFromTheTempFile(reader: FileReader) {
 *     var builder = new StringBuilder
 *     var c = reader.read()
 *     while (c != -1) {
 *       builder.append(c.toChar)
 *       c = reader.read()
 *     }
 *     assert(builder.toString === "Hello, test!")
 *   }
 * 
 *   def testFirstCharOfTheTempFile(reader: FileReader) {
 *     assert(reader.read() === 'H')
 *   }
 * 
 *   def testWithoutAFixture() {
 *     assert(1 + 1 === 2)
 *   }
 * }
 * 
* *

* It is worth noting that the only difference in the test code between the mutable * BeforeAndAfterEach approach shown previously and the immutable FixtureSuite * approach shown here is that two of the FixtureSuite's test methods take a FileReader as * a parameter. Otherwise the test code is identical. One benefit of the explicit parameter is that, as demonstrated * by the testWithoutAFixture method, a FixtureSuite * test method need not take the fixture. (Tests that don't take a fixture as a parameter are passed to the withFixture * that takes a NoArgTest, shown previously.) So you can have some tests that take a fixture, and others that don't. * In this case, the FixtureSuite provides documentation indicating which * test methods use the fixture and which don't, whereas the BeforeAndAfterEach approach does not. *

* *

* If you want to execute code before and after all tests (and nested suites) in a suite, such * as you could do with @BeforeClass and @AfterClass * annotations in JUnit 4, you can use the beforeAll and afterAll * methods of BeforeAndAfterAll. See the documentation for BeforeAndAfterAll for * an example. *

* *

The config map

* *

* In some cases you may need to pass information to a suite of tests. * For example, perhaps a suite of tests needs to grab information from a file, and you want * to be able to specify a different filename during different runs. You can accomplish this in ScalaTest by passing * the filename in the config map of key-value pairs, which is passed to run as a Map[String, Any]. * The values in the config map are called "config objects," because they can be used to configure * suites, reporters, and tests. *

* *

* You can specify a string config object is via the ScalaTest Runner, either via the command line * or ScalaTest's ant task. * (See the documentation for Runner for information on how to specify * config objects on the command line.) * The config map is passed to run, runNestedSuites, runTests, and runTest, * so one way to access it in your suite is to override one of those methods. If you need to use the config map inside your tests, you * can use one of the traits in the org.scalatest.fixture package. (See the * documentation for FixtureSuite * for instructions on how to access the config map in tests.) *

* *

Tagging tests

* *

* A Suite's tests may be classified into groups by tagging them with string names. When executing * a Suite, groups of tests can optionally be included and/or excluded. In this * trait's implementation, tags are indicated by annotations attached to the test method. To * create a new tag type to use in Suites, simply define a new Java annotation that itself is annotated with the org.scalatest.TagAnnotation annotation. * (Currently, for annotations to be * visible in Scala programs via Java reflection, the annotations themselves must be written in Java.) For example, * to create a tag named SlowAsMolasses, to use to mark slow tests, you would * write in Java: *

* *

BECAUSE OF A SCALADOC BUG IN SCALA 2.8, I HAD TO PUT A SPACE AFTER THE AT SIGN IN ONE THE TARGET ANNOTATION EXAMPLE BELOW. IF YOU * WANT TO COPY AND PASTE FROM THIS EXAMPLE, YOU'LL NEED TO REMOVE THE SPACE BY HAND, OR COPY FROM * THE SUITE SCALADOC FOR VERSION 1.1 INSTEAD, WHICH IS ALSO VALID FOR 1.3. - Bill Venners

* *
 * import java.lang.annotation.*; 
 * import org.scalatest.TagAnnotation
 * 
 * @TagAnnotation
 * @Retention(RetentionPolicy.RUNTIME)
 * @ Target({ElementType.METHOD, ElementType.TYPE})
 * public @interface SlowAsMolasses {}
 * 
* *

* Given this new annotation, you could place a Suite test method into the SlowAsMolasses group * (i.e., tag it as being SlowAsMolasses) like this: *

* *
 * @SlowAsMolasses
 * def testSleeping() = sleep(1000000)
 * 
* *

* The primary run method takes a Filter, whose constructor takes an optional * Set[String]s called tagsToInclude and a Set[String] called * tagsToExclude. If tagsToInclude is None, all tests will be run * except those those belonging to tags listed in the * tagsToExclude Set. If tagsToInclude is defined, only tests * belonging to tags mentioned in the tagsToInclude set, and not mentioned in tagsToExclude, * will be run. *

* *

Ignored tests

* *

* Another common use case is that tests must be “temporarily” disabled, with the * good intention of resurrecting the test at a later time. ScalaTest provides an Ignore * annotation for this purpose. You use it like this: *

* *
 * import org.scalatest.Suite
 * import org.scalatest.Ignore
 *
 * class MySuite extends Suite {
 *
 *   def testAddition() {
 *     val sum = 1 + 1
 *     assert(sum === 2)
 *     assert(sum + 2 === 4)
 *   }
 *
 *   @Ignore
 *   def testSubtraction() {
 *     val diff = 4 - 1
 *     assert(diff === 3)
 *     assert(diff - 2 === 1)
 *   }
 * }
 * 
* *

* If you run this version of MySuite with: *

* *
 * scala> (new MySuite).run()
 * 
* *

* It will run only testAddition and report that testSubtraction was ignored. You'll see: *

* *
 * Test Starting - MySuite: testAddition
 * Test Succeeded - MySuite: testAddition
 * Test Ignored - MySuite: testSubtraction
 * 
* *

* Ignore is implemented as a tag. The Filter class effectively * adds org.scalatest.Ignore to the tagsToExclude Set if it not already * in the tagsToExclude set passed to its primary constructor. The only difference between * org.scalatest.Ignore and the tags you may define and exclude is that ScalaTest reports * ignored tests to the Reporter. The reason ScalaTest reports ignored tests is as a feeble * attempt to encourage ignored tests to be eventually fixed and added back into the active suite of tests. *

* *

Pending tests

* *

* A pending test is one that has been given a name but is not yet implemented. The purpose of * pending tests is to facilitate a style of testing in which documentation of behavior is sketched * out before tests are written to verify that behavior (and often, the before the behavior of * the system being tested is itself implemented). Such sketches form a kind of specification of * what tests and functionality to implement later. *

* *

* To support this style of testing, a test can be given a name that specifies one * bit of behavior required by the system being tested. The test can also include some code that * sends more information about the behavior to the reporter when the tests run. At the end of the test, * it can call method pending, which will cause it to complete abruptly with TestPendingException. * Because tests in ScalaTest can be designated as pending with TestPendingException, both the test name and any information * sent to the reporter when running the test can appear in the report of a test run. (In other words, * the code of a pending test is executed just like any other test.) However, because the test completes abruptly * with TestPendingException, the test will be reported as pending, to indicate * the actual test, and possibly the functionality it is intended to test, has not yet been implemented. *

* *

* Although pending tests may be used more often in specification-style suites, such as * org.scalatest.Spec, you can also use it in Suite, like this: *

* *
 * import org.scalatest.Suite
 *
 * class MySuite extends Suite {
 *
 *   def testAddition() {
 *     val sum = 1 + 1
 *     assert(sum === 2)
 *     assert(sum + 2 === 4)
 *   }
 *
 *   def testSubtraction() { pending }
 * }
 * 
* *

* If you run this version of MySuite with: *

* *
 * scala> (new MySuite).run()
 * 
* *

* It will run both tests but report that testSubtraction is pending. You'll see: *

* *
 * Test Starting - MySuite: testAddition
 * Test Succeeded - MySuite: testAddition
 * Test Starting - MySuite: testSubtraction
 * Test Pending - MySuite: testSubtraction
 * 
* *

Informers

* *

* One of the parameters to the primary run method is an Reporter, which * will collect and report information about the running suite of tests. * Information about suites and tests that were run, whether tests succeeded or failed, * and tests that were ignored will be passed to the Reporter as the suite runs. * Most often the reporting done by default by Suite's methods will be sufficient, but * occasionally you may wish to provide custom information to the Reporter from a test method. * For this purpose, you can optionally include an Informer parameter in a test method, and then * pass the extra information to the Informer via its apply method. The Informer * will then pass the information to the Reporter by sending an InfoProvided event. * Here's an example: *

* *
 * import org.scalatest._
 * 
 * class MySuite extends Suite {
 *   def testAddition(info: Informer) {
 *     assert(1 + 1 === 2)
 *     info("Addition seems to work")
 *   }
 * }
 * 
* * If you run this Suite from the interpreter, you will see the message * included in the printed report: * *
 * scala> (new MySuite).run()
 * Test Starting - MySuite: testAddition(Reporter)
 * Info Provided - MySuite: testAddition(Reporter)
 *   Addition seems to work
 * Test Succeeded - MySuite: testAddition(Reporter)
 * 
* *

Executing suites in parallel

* *

* The primary run method takes as its last parameter an optional Distributor. If * a Distributor is passed in, this trait's implementation of run puts its nested * Suites into the distributor rather than executing them directly. The caller of run * is responsible for ensuring that some entity runs the Suites placed into the * distributor. The -c command line parameter to Runner, for example, will cause * Suites put into the Distributor to be run in parallel via a pool of threads. *

* *

Treatement of java.lang.Errors

* *

* The Javadoc documentation for java.lang.Error states: *

* *
* An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most * such errors are abnormal conditions. *
* *

* Because Errors are used to denote serious errors, trait Suite and its subtypes in the ScalaTest API do not always treat a test * that completes abruptly with an Error as a test failure, but sometimes as an indication that serious problems * have arisen that should cause the run to abort. For example, if a test completes abruptly with an OutOfMemoryError, * it will not be reported as a test failure, but will instead cause the run to abort. Because not everyone uses Errors only to represent serious * problems, however, ScalaTest only behaves this way for the following exception types (and their subclasses): *

* *

    *
  • java.lang.annotation.AnnotationFormatError
  • *
  • java.awt.AWTError
  • *
  • java.nio.charset.CoderMalfunctionError
  • *
  • javax.xml.parsers.FactoryConfigurationError
  • *
  • java.lang.LinkageError
  • *
  • java.lang.ThreadDeath
  • *
  • javax.xml.transform.TransformerFactoryConfigurationError
  • *
  • java.lang.VirtualMachineError
  • *
* *

* The previous list includes all Errors that exist as part of Java 1.5 API, excluding java.lang.AssertionError. ScalaTest * does treat a thrown AssertionError as an indication of a test failure. In addition, any other Error that is not an instance of a * type mentioned in the previous list will be caught by the Suite traits in the ScalaTest API and reported as the cause of a test failure. *

* *

* Although trait Suite and all its subtypes in the ScalaTest API consistently behave this way with regard to Errors, * this behavior is not required by the contract of Suite. Subclasses and subtraits that you define, for example, may treat all * Errors as test failures, or indicate errors in some other way that has nothing to do with exceptions. *

* *

Extensibility

* *

* Trait Suite provides default implementations of its methods that should * be sufficient for most applications, but many methods can be overridden when desired. Here's * a summary of the methods that are intended to be overridden: *

* *
    *
  • run - override this method to define custom ways to run suites of * tests.
  • *
  • runNestedSuites - override this method to define custom ways to run nested suites.
  • *
  • runTests - override this method to define custom ways to run a suite's tests.
  • *
  • runTest - override this method to define custom ways to run a single named test.
  • *
  • testNames - override this method to specify the Suite's test names in a custom way.
  • *
  • tags - override this method to specify the Suite's test tags in a custom way.
  • *
  • nestedSuites - override this method to specify the Suite's nested Suites in a custom way.
  • *
  • suiteName - override this method to specify the Suite's name in a custom way.
  • *
  • expectedTestCount - override this method to count this Suite's expected tests in a custom way.
  • *
* *

* For example, this trait's implementation of testNames performs reflection to discover methods starting with test, * and places these in a Set whose iterator returns the names in alphabetical order. If you wish to run tests in a different * order in a particular Suite, perhaps because a test named testAlpha can only succeed after a test named * testBeta has run, you can override testNames so that it returns a Set whose iterator returns * testBeta before testAlpha. (This trait's implementation of run will invoke tests * in the order they come out of the testNames Set iterator.) *

* *

* Alternatively, you may not like starting your test methods with test, and prefer using @Test annotations in * the style of Java's JUnit 4 or TestNG. If so, you can override testNames to discover tests using either of these two APIs * @Test annotations, or one of your own invention. (This is in fact * how org.scalatest.junit.JUnitSuite and org.scalatest.testng.TestNGSuite work.) *

* *

* Moreover, test in ScalaTest does not necessarily mean test method. A test can be anything that can be given a name, * that starts and either succeeds or fails, and can be ignored. In org.scalatest.FunSuite, for example, tests are represented * as function values. This * approach might look foreign to JUnit users, but may feel more natural to programmers with a functional programming background. * To facilitate this style of writing tests, FunSuite overrides testNames, runTest, and run such that you can * define tests as function values. *

* *

* You can also model existing JUnit 3, JUnit 4, or TestNG tests as suites of tests, thereby incorporating tests written in Java into a ScalaTest suite. * The "wrapper" classes in packages org.scalatest.junit and org.scalatest.testng exist to make this easy. * No matter what legacy tests you may have, it is likely you can create or use an existing Suite subclass that allows you to model those tests * as ScalaTest suites and tests and incorporate them into a ScalaTest suite. You can then write new tests in Scala and continue supporting * older tests in Java. *

* * @author Bill Venners */ @serializable trait Suite extends Assertions with AbstractSuite { thisSuite => import Suite.TestMethodPrefix, Suite.InformerInParens, Suite.IgnoreAnnotation /* * @param nestedSuites A List of Suite * objects. The specified List must be non-empty. Each element must be non-null and an instance * of org.scalatest.Suite. * * @throws NullPointerException if nestedSuites * is null or any element of nestedSuites * set is null. */ /** * A test function taking no arguments, which also provides a test name and config map. * *

* Suite's implementation of runTest passes instances of this trait * to withFixture for every test method it executes. It invokes withFixture * for every test, including test methods that take an Informer. For the latter case, * the Informer to pass to the test method is already contained inside the * NoArgTest instance passed to withFixture. *

*/ protected trait NoArgTest extends (() => Unit) { /** * The name of this test. */ def name: String /** * Runs the code of the test. */ def apply() /** * A Map[String, Any] containing objects that can be used * to configure the fixture and test. */ def configMap: Map[String, Any] } // should nestedSuites return a Set[String] instead? /** * A List of this Suite object's nested Suites. If this Suite contains no nested Suites, * this method returns an empty List. This trait's implementation of this method returns an empty List. */ def nestedSuites: List[Suite] = Nil /** * Executes this Suite, printing results to the standard output. * *

* This method implementation calls run on this Suite, passing in: *

* *
    *
  • testName - None
  • *
  • reporter - a reporter that prints to the standard output
  • *
  • stopper - a Stopper whose apply method always returns false
  • *
  • filter - a Filter constructed with None for tagsToInclude and Set() * for tagsToExclude
  • *
  • configMap - an empty Map[String, Any]
  • *
  • distributor - None
  • *
  • tracker - a new Tracker
  • *
* *

* This method serves as a convenient way to execute a Suite, especially from * within the Scala interpreter. *

* *

* Note: In ScalaTest, the terms "execute" and "run" basically mean the same thing and * can be used interchangably. The reason this convenience method and its three overloaded forms * aren't named run * is because junit.framework.TestCase declares a run method * that takes no arguments but returns a junit.framework.TestResult. That * run method would not overload with this method if it were named run, * because it would have the same parameters but a different return type than the one * defined in TestCase. To facilitate integration with JUnit 3, therefore, * these convenience "run" methods are named execute. In particular, this allows trait * org.scalatest.junit.JUnit3Suite to extend both org.scalatest.Suite and * junit.framework.TestCase, which enables the creating of classes that * can be run with either ScalaTest or JUnit 3. *

*/ final def execute() { run(None, new StandardOutReporter, new Stopper {}, Filter(), Map(), None, new Tracker) } /** * Executes this Suite with the specified configMap, printing results to the standard output. * *

* This method implementation calls run on this Suite, passing in: *

* *
    *
  • testName - None
  • *
  • reporter - a reporter that prints to the standard output
  • *
  • stopper - a Stopper whose apply method always returns false
  • *
  • filter - a Filter constructed with None for tagsToInclude and Set() * for tagsToExclude
  • *
  • configMap - the specified configMap Map[String, Any]
  • *
  • distributor - None
  • *
  • tracker - a new Tracker
  • *
* *

* This method serves as a convenient way to execute a Suite, passing in some objects via the configMap, especially from within the Scala interpreter. *

* *

* Note: In ScalaTest, the terms "execute" and "run" basically mean the same thing and * can be used interchangably. The reason this convenience method and its three overloaded forms * aren't named run is described the documentation of the overloaded form that * takes no parameters: execute(). *

* * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * * @throws NullPointerException if the passed configMap parameter is null. */ final def execute(configMap: Map[String, Any]) { run(None, new StandardOutReporter, new Stopper {}, Filter(), configMap, None, new Tracker) } /** * Executes the test specified as testName in this Suite, printing results to the standard output. * *

* This method implementation calls run on this Suite, passing in: *

* *
    *
  • testName - Some(testName)
  • *
  • reporter - a reporter that prints to the standard output
  • *
  • stopper - a Stopper whose apply method always returns false
  • *
  • filter - a Filter constructed with None for tagsToInclude and Set() * for tagsToExclude
  • *
  • configMap - an empty Map[String, Any]
  • *
  • distributor - None
  • *
  • tracker - a new Tracker
  • *
* *

* This method serves as a convenient way to run a single test, especially from within the Scala interpreter. *

* *

* Note: In ScalaTest, the terms "execute" and "run" basically mean the same thing and * can be used interchangably. The reason this convenience method and its three overloaded forms * aren't named run is described the documentation of the overloaded form that * takes no parameters: execute(). *

* * @param testName the name of one test to run. * * @throws NullPointerException if the passed testName parameter is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ final def execute(testName: String) { run(Some(testName), new StandardOutReporter, new Stopper {}, Filter(), Map(), None, new Tracker) } /** * Executes the test specified as testName in this Suite with the specified configMap, printing * results to the standard output. * *

* This method implementation calls run on this Suite, passing in: *

* *
    *
  • testName - Some(testName)
  • *
  • reporter - a reporter that prints to the standard output
  • *
  • stopper - a Stopper whose apply method always returns false
  • *
  • filter - a Filter constructed with None for tagsToInclude and Set() * for tagsToExclude
  • *
  • configMap - the specified configMap Map[String, Any]
  • *
  • distributor - None
  • *
  • tracker - a new Tracker
  • *
* *

* This method serves as a convenient way to execute a single test, passing in some objects via the configMap, especially from * within the Scala interpreter. *

* *

* Note: In ScalaTest, the terms "execute" and "run" basically mean the same thing and * can be used interchangably. The reason this convenience method and its three overloaded forms * aren't named run is described the documentation of the overloaded form that * takes no parameters: execute(). *

* * @param testName the name of one test to run. * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * * @throws NullPointerException if either of the passed testName or configMap parameters is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ final def execute(testName: String, configMap: Map[String, Any]) { run(Some(testName), new StandardOutReporter, new Stopper {}, Filter(), configMap, None, new Tracker) } /** * A Map whose keys are String tag names with which tests in this Suite are marked, and * whose values are the Set of test names marked with each tag. If this Suite contains no tags, this * method returns an empty Map. * *

* This trait's implementation of this method uses Java reflection to discover any Java annotations attached to its test methods. The * fully qualified name of each unique annotation that extends TagAnnotation is considered a tag. This trait's * implementation of this method, therefore, places one key/value pair into to the * Map for each unique tag annotation name discovered through reflection. The mapped value for each tag name key will contain * the test method name, as provided via the testNames method. *

* *

* Subclasses may override this method to define and/or discover tags in a custom manner, but overriding method implementations * should never return an empty Set as a value. If a tag has no tests, its name should not appear as a key in the * returned Map. *

* *

* Note, the TagAnnotation annotation was introduced in ScalaTest 1.0, when "groups" were renamed * to "tags." In 1.0 and 1.1, the TagAnnotation will continue to not be required by an annotation on a Suite * method. Any annotation on a Suite method will be considered a tag until 1.2, to give users time to add * TagAnnotations on any tag annotations they made prior to the 1.0 release. From 1.2 onward, only annotations * themselves annotated by TagAnnotation will be considered tag annotations. *

*/ def tags: Map[String, Set[String]] = { def getTags(testName: String) = /* AFTER THE DEPRECATION CYCLE FOR GROUPS TO TAGS (1.2), REPLACE THE FOLLOWING FOR LOOP WITH THIS COMMENTED OUT ONE THAT MAKES SURE ANNOTATIONS ARE TAGGED WITH TagAnnotation. for { a <- getMethodForTestName(testName).getDeclaredAnnotations annotationClass = a.annotationType if annotationClass.isAnnotationPresent(classOf[TagAnnotation]) } yield annotationClass.getName */ for (a <- getMethodForTestName(testName).getDeclaredAnnotations) yield a.annotationType.getName val elements = for (testName <- testNames; if !getTags(testName).isEmpty) yield testName -> (Set() ++ getTags(testName)) Map() ++ elements } /** * The groups methods has been deprecated and will be removed in a future version of ScalaTest. * Please call (and override) tags instead. */ @deprecated // deprecated in 1.0, so remove in 1.4 final def groups: Map[String, Set[String]] = tags /** * An Set of test names. If this Suite contains no tests, this method returns an empty Set. * *

* This trait's implementation of this method uses Java reflection to discover all public methods whose name starts with "test", * which take either nothing or a single Informer as parameters. For each discovered test method, it assigns a test name * comprised of just the method name if the method takes no parameters, or the method name plus (Informer) if the * method takes a Informer. Here are a few method signatures and the names that this trait's implementation assigns them: *

* *
  * def testCat() {}         // test name: "testCat"
  * def testCat(Informer) {} // test name: "testCat(Informer)"
  * def testDog() {}         // test name: "testDog"
  * def testDog(Informer) {} // test name: "testDog(Informer)"
  * def test() {}            // test name: "test"
  * def test(Informer) {}    // test name: "test(Informer)"
  * 
* *

* This trait's implementation of this method returns an immutable Set of all such names, excluding the name * testNames. The iterator obtained by invoking elements on this * returned Set will produce the test names in their natural order, as determined by String's * compareTo method. *

* *

* This trait's implementation of runTests invokes this method * and calls runTest for each test name in the order they appear in the returned Set's iterator. * Although this trait's implementation of this method returns a Set whose iterator produces String * test names in a well-defined order, the contract of this method does not required a defined order. Subclasses are free to * override this method and return test names in an undefined order, or in a defined order that's different from String's * natural order. *

* *

* Subclasses may override this method to produce test names in a custom manner. One potential reason to override testNames is * to run tests in a different order, for example, to ensure that tests that depend on other tests are run after those other tests. * Another potential reason to override is allow tests to be defined in a different manner, such as methods annotated @Test annotations * (as is done in JUnitSuite and TestNGSuite) or test functions registered during construction (as is * done in FunSuite and Spec). *

*/ def testNames: Set[String] = { def takesInformer(m: Method) = { val paramTypes = m.getParameterTypes paramTypes.length == 1 && classOf[Informer].isAssignableFrom(paramTypes(0)) } def isTestMethod(m: Method) = { val isInstanceMethod = !Modifier.isStatic(m.getModifiers()) // name must have at least 4 chars (minimum is "test") val simpleName = m.getName val firstFour = if (simpleName.length >= 4) simpleName.substring(0, 4) else "" val paramTypes = m.getParameterTypes val hasNoParams = paramTypes.length == 0 // Discover testNames(Informer) because if we didn't it might be confusing when someone // actually wrote a testNames(Informer) method and it was silently ignored. val isTestNames = simpleName == "testNames" isInstanceMethod && (firstFour == "test") && ((hasNoParams && !isTestNames) || takesInformer(m)) } val testNameArray = for (m <- getClass.getMethods; if isTestMethod(m)) yield if (takesInformer(m)) m.getName + InformerInParens else m.getName TreeSet[String]() ++ testNameArray } private def testMethodTakesInformer(testName: String) = testName.endsWith(InformerInParens) private def getMethodForTestName(testName: String) = getClass.getMethod( simpleNameForTest(testName), (if (testMethodTakesInformer(testName)) Array(classOf[Informer]) else new Array[Class[_]](0)): _* ) /** * Run the passed test function in the context of a fixture established by this method. * *

* This method should set up the fixture needed by the tests of the * current suite, invoke the test function, and if needed, perform any clean * up needed after the test completes. Because the NoArgTest function * passed to this method takes no parameters, preparing the fixture will require * side effects, such as reassigning instance vars in this Suite or initializing * a globally accessible external database. If you want to avoid reassigning instance vars * you can use FixtureSuite. *

* *

* This trait's implementation of runTest invokes this method for each test, passing * in a NoArgTest whose apply method will execute the code of the test. *

* *

* This trait's implementation of this method simply invokes the passed NoArgTest function. *

* * @param test the no-arg test function to run with a fixture */ protected def withFixture(test: NoArgTest) { test() } /** * Run a test. * *

* This trait's implementation uses Java reflection to invoke on this object the test method identified by the passed testName. *

* *

* Implementations of this method are responsible for ensuring a TestStarting event * is fired to the Reporter before executing any test, and either TestSucceeded, * TestFailed, or TestPending after executing any nested * Suite. (If a test is marked with the org.scalatest.Ignore tag, the * runTests method is responsible for ensuring a TestIgnored event is fired and that * this runTest method is not invoked for that ignored test.) *

* * @param testName the name of one test to run. * @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 key-value pairs that can be used by the executing Suite of tests. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * @throws NullPointerException if any of testName, reporter, stopper, configMap * or tracker is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ protected def runTest(testName: String, reporter: Reporter, stopper: Stopper, configMap: Map[String, Any], tracker: Tracker) { if (testName == null || reporter == null || stopper == null || configMap == null || tracker == null) throw new NullPointerException val stopRequested = stopper val report = wrapReporterIfNecessary(reporter) val method = try { getMethodForTestName(testName) } catch { case e: NoSuchMethodException => throw new IllegalArgumentException(Resources("testNotFound", testName)) case e => throw e } // Create a Rerunner if the Suite has a no-arg constructor val hasPublicNoArgConstructor = Suite.checkForPublicNoArgConstructor(getClass) val rerunnable = if (hasPublicNoArgConstructor) Some(new TestRerunner(getClass.getName, testName)) else None val testStartTime = System.currentTimeMillis report(TestStarting(tracker.nextOrdinal(), thisSuite.suiteName, Some(thisSuite.getClass.getName), testName, None, rerunnable)) val args: Array[Object] = if (testMethodTakesInformer(testName)) { val informer = new Informer { def apply(message: String) { if (message == null) throw new NullPointerException report(InfoProvided(tracker.nextOrdinal(), message, Some(NameInfo(thisSuite.suiteName, Some(thisSuite.getClass.getName), Some(testName))))) } } Array(informer) } else Array() try { val theConfigMap = configMap withFixture( new NoArgTest { def name = testName def apply() { method.invoke(thisSuite, args: _*) } def configMap = theConfigMap } ) val duration = System.currentTimeMillis - testStartTime report(TestSucceeded(tracker.nextOrdinal(), thisSuite.suiteName, Some(thisSuite.getClass.getName), testName, Some(duration), None, rerunnable)) } catch { case ite: InvocationTargetException => val t = ite.getTargetException t match { case _: TestPendingException => report(TestPending(tracker.nextOrdinal(), thisSuite.suiteName, Some(thisSuite.getClass.getName), testName)) case e if !anErrorThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(t, hasPublicNoArgConstructor, testName, rerunnable, report, tracker, duration) case e => throw e } case e if !anErrorThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(e, hasPublicNoArgConstructor, testName, rerunnable, report, tracker, duration) case e => throw e } } /** * Run zero to many of this Suite's tests. * *

* This method takes a testName parameter that optionally specifies a test to invoke. * If testName is defined, this trait's implementation of this method * invokes runTest on this object, passing in: *

* *
    *
  • testName - the String value of the testName Option passed * to this method
  • *
  • reporter - the Reporter passed to this method, or one that wraps and delegates to it
  • *
  • stopper - the Stopper passed to this method, or one that wraps and delegates to it
  • *
  • configMap - the configMap Map passed to this method, or one that wraps and delegates to it
  • *
* *

* This method takes a Filter, which encapsulates an optional Set of tag names that should be included * (tagsToInclude) and a Set that should be excluded (tagsToExclude), when deciding which * of this Suite's tests to run. * If tagsToInclude is None, all tests will be run * except those those belonging to tags listed in the tagsToExclude Set. If tagsToInclude is defined, only tests * belonging to tags mentioned in the tagsToInclude Set, and not mentioned in the tagsToExcludeSet * will be run. However, if testName is defined, tagsToInclude and tagsToExclude are essentially ignored. * Only if testName is None will tagsToInclude and tagsToExclude be consulted to * determine which of the tests named in the testNames Set should be run. This trait's implementation * behaves this way, and it is part of the general contract of this method, so all overridden forms of this method should behave * this way as well. For more information on test tags, see the main documentation for this trait and for class Filter. * Note that this means that even if a test is marked as ignored, for example a test method in a Suite annotated with * org.scalatest.Ignore, if that test name is passed as testName to runTest, it will be invoked * despite the Ignore annotation. *

* *

* If testName is None, this trait's implementation of this method * invokes testNames on this Suite to get a Set of names of tests to potentially run. * (A testNames value of None essentially acts as a wildcard that means all tests in * this Suite that are selected by tagsToInclude and tagsToExclude should be run.) * For each test in the testName Set, in the order * they appear in the iterator obtained by invoking the elements method on the Set, this trait's implementation * of this method checks whether the test should be run based on the Filter. * If so, this implementation invokes runTest, passing in: *

* *
    *
  • testName - the String name of the test to run (which will be one of the names in the testNames Set)
  • *
  • reporter - the Reporter passed to this method, or one that wraps and delegates to it
  • *
  • stopper - the Stopper passed to this method, or one that wraps and delegates to it
  • *
  • configMap - the configMap passed to this method, or one that wraps and delegates to it
  • *
* *

* If a test is marked with the org.scalatest.Ignore tag, implementations * of this method are responsible for ensuring a TestIgnored event is fired for that test * and that runTest is not called for that test. *

* * @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 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 filter a Filter with which to filter tests based on their tags * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param distributor an optional Distributor, into which to put nested Suites to be run * by another entity, such as concurrently by a pool of threads. If None, nested Suites will be run sequentially. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * @throws NullPointerException if any of the passed parameters is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ protected def runTests(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { if (testName == null) throw new NullPointerException("testName was null") if (reporter == null) throw new NullPointerException("reporter was null") if (stopper == null) throw new NullPointerException("stopper was null") if (filter == null) throw new NullPointerException("filter was null") if (configMap == null) throw new NullPointerException("configMap was null") if (distributor == null) throw new NullPointerException("distributor was null") if (tracker == null) throw new NullPointerException("tracker was null") val stopRequested = stopper // Wrap any non-DispatchReporter, non-CatchReporter in a CatchReporter, // so that exceptions are caught and transformed // into error messages on the standard error stream. val report = wrapReporterIfNecessary(reporter) // If a testName is passed to run, just run that, else run the tests returned // by testNames. testName match { case Some(tn) => runTest(tn, report, stopRequested, configMap, tracker) case None => for ((tn, ignoreTest) <- filter(testNames, tags)) if (ignoreTest) report(TestIgnored(tracker.nextOrdinal(), thisSuite.suiteName, Some(thisSuite.getClass.getName), tn)) else runTest(tn, report, stopRequested, configMap, tracker) } } /** * Runs this suite of tests. * *

If testName is None, this trait's implementation of this method * calls these two methods on this object in this order:

* *
    *
  1. runNestedSuites(report, stopper, tagsToInclude, tagsToExclude, configMap, distributor)
  2. *
  3. runTests(testName, report, stopper, tagsToInclude, tagsToExclude, configMap)
  4. *
* *

* If testName is defined, then this trait's implementation of this method * calls runTests, but does not call runNestedSuites. This behavior * is part of the contract of this method. Subclasses that override run must take * care not to call runNestedSuites if testName is defined. (The * OneInstancePerTest trait depends on this behavior, for example.) *

* *

* Subclasses and subtraits that override this run method can implement them without * invoking either the runTests or runNestedSuites methods, which * are invoked by this trait's implementation of this method. It is recommended, but not required, * that subclasses and subtraits that override run in a way that does not * invoke runNestedSuites also override runNestedSuites and make it * final. Similarly it is recommended, but not required, * that subclasses and subtraits that override run in a way that does not * invoke runTests also override runTests (and runTest, * which this trait's implementation of runTests calls) and make it * final. The implementation of these final methods can either invoke the superclass implementation * of the method, or throw an UnsupportedOperationException if appropriate. The * reason for this recommendation is that ScalaTest includes several traits that override * these methods to allow behavior to be mixed into a Suite. For example, trait * BeforeAndAfterEach overrides runTestss. In a Suite * subclass that no longer invokes runTests from run, the * BeforeAndAfterEach trait is not applicable. Mixing it in would have no effect. * By making runTests final in such a Suite subtrait, you make * the attempt to mix BeforeAndAfterEach into a subclass of your subtrait * a compiler error. (It would fail to compile with a complaint that BeforeAndAfterEach * is trying to override runTests, which is a final method in your 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 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 filter a Filter with which to filter tests based on their tags * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param distributor an optional Distributor, into which to put nested Suites to be run * by another entity, such as concurrently by a pool of threads. If None, nested Suites will be run sequentially. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * * @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 */ def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { if (testName == null) throw new NullPointerException("testName was null") if (reporter == null) throw new NullPointerException("reporter was null") if (stopper == null) throw new NullPointerException("stopper was null") if (filter == null) throw new NullPointerException("filter was null") if (configMap == null) throw new NullPointerException("configMap was null") if (distributor == null) throw new NullPointerException("distributor was null") if (tracker == null) throw new NullPointerException("tracker was null") val stopRequested = stopper val report = wrapReporterIfNecessary(reporter) testName match { case None => runNestedSuites(report, stopRequested, filter, configMap, distributor, tracker) case Some(_) => } runTests(testName, report, stopRequested, filter, configMap, distributor, tracker) if (stopRequested()) { val rawString = Resources("executeStopping") report(InfoProvided(tracker.nextOrdinal(), rawString, Some(NameInfo(thisSuite.suiteName, Some(thisSuite.getClass.getName), testName)))) } } private def handleFailedTest(throwable: Throwable, hasPublicNoArgConstructor: Boolean, testName: String, rerunnable: Option[Rerunner], report: Reporter, tracker: Tracker, duration: Long) { val message = if (throwable.getMessage != null) // [bv: this could be factored out into a helper method] throwable.getMessage else throwable.toString report(TestFailed(tracker.nextOrdinal(), message, thisSuite.suiteName, Some(thisSuite.getClass.getName), testName, Some(throwable), Some(duration), None, rerunnable)) } /** * * Run zero to many of this Suite's nested Suites. * *

* If the passed distributor is None, this trait's * implementation of this method invokes run on each * nested Suite in the List obtained by invoking nestedSuites. * If a nested Suite's run * method completes abruptly with an exception, this trait's implementation of this * method reports that the Suite aborted and attempts to run the * next nested Suite. * If the passed distributor is defined, this trait's implementation * puts each nested Suite * into the Distributor contained in the Some, in the order in which the * Suites appear in the List returned by nestedSuites, passing * in a new Tracker obtained by invoking nextTracker on the Tracker * passed to this method. *

* *

* Implementations of this method are responsible for ensuring SuiteStarting events * are fired to the Reporter before executing any nested Suite, and either SuiteCompleted * or SuiteAborted after executing any nested Suite. *

* * @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 filter a Filter with which to filter tests based on their tags * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param distributor an optional Distributor, into which to put nested Suites to be run * by another entity, such as concurrently by a pool of threads. If None, nested Suites will be run sequentially. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * * @throws NullPointerException if any passed parameter is null. */ protected def runNestedSuites(reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { if (reporter == null) throw new NullPointerException("reporter was null") if (stopper == null) throw new NullPointerException("stopper was null") if (filter == null) throw new NullPointerException("filter was null") if (configMap == null) throw new NullPointerException("configMap was null") if (distributor == null) throw new NullPointerException("distributor was null") if (tracker == null) throw new NullPointerException("tracker was null") val stopRequested = stopper val report = wrapReporterIfNecessary(reporter) def callExecuteOnSuite(nestedSuite: Suite) { if (!stopRequested()) { // Create a Rerunner if the Suite has a no-arg constructor val hasPublicNoArgConstructor = Suite.checkForPublicNoArgConstructor(nestedSuite.getClass) val rerunnable = if (hasPublicNoArgConstructor) Some(new SuiteRerunner(nestedSuite.getClass.getName)) else None val rawString = Resources("suiteExecutionStarting") val formatter = formatterForSuiteStarting(nestedSuite) val suiteStartTime = System.currentTimeMillis report(SuiteStarting(tracker.nextOrdinal(), nestedSuite.suiteName, Some(nestedSuite.getClass.getName), formatter, rerunnable)) try { // Same thread, so OK to send same tracker nestedSuite.run(None, report, stopRequested, filter, configMap, distributor, tracker) val rawString = Resources("suiteCompletedNormally") val formatter = formatterForSuiteCompleted(nestedSuite) val duration = System.currentTimeMillis - suiteStartTime report(SuiteCompleted(tracker.nextOrdinal(), nestedSuite.suiteName, Some(thisSuite.getClass.getName), Some(duration), formatter, rerunnable)) } catch { case e: RuntimeException => { val rawString = Resources("executeException") val formatter = formatterForSuiteAborted(nestedSuite, rawString) val duration = System.currentTimeMillis - suiteStartTime report(SuiteAborted(tracker.nextOrdinal(), rawString, nestedSuite.suiteName, Some(thisSuite.getClass.getName), Some(e), Some(duration), formatter, rerunnable)) } } } } distributor match { case None => nestedSuites.foreach(callExecuteOnSuite) case Some(distribute) => for (nestedSuite <- nestedSuites) distribute(nestedSuite, tracker.nextTracker()) } } /** * A user-friendly suite name for this Suite. * *

* This trait's * implementation of this method returns the simple name of this object's class. This * trait's implementation of runNestedSuites calls this method to obtain a * name for Reports to pass to the suiteStarting, suiteCompleted, * and suiteAborted methods of the Reporter. *

* * @return this Suite object's suite name. */ def suiteName = getSimpleNameOfAnObjectsClass(thisSuite) /** * Throws TestPendingException to indicate a test is pending. * *

* A pending test is one that has been given a name but is not yet implemented. The purpose of * pending tests is to facilitate a style of testing in which documentation of behavior is sketched * out before tests are written to verify that behavior (and often, the before the behavior of * the system being tested is itself implemented). Such sketches form a kind of specification of * what tests and functionality to implement later. *

* *

* To support this style of testing, a test can be given a name that specifies one * bit of behavior required by the system being tested. The test can also include some code that * sends more information about the behavior to the reporter when the tests run. At the end of the test, * it can call method pending, which will cause it to complete abruptly with TestPendingException. * Because tests in ScalaTest can be designated as pending with TestPendingException, both the test name and any information * sent to the reporter when running the test can appear in the report of a test run. (In other words, * the code of a pending test is executed just like any other test.) However, because the test completes abruptly * with TestPendingException, the test will be reported as pending, to indicate * the actual test, and possibly the functionality it is intended to test, has not yet been implemented. *

* *

* Note: This method always completes abruptly with a TestPendingException. Thus it always has a side * effect. Methods with side effects are usually invoked with parentheses, as in pending(). This * method is defined as a parameterless method, in flagrant contradiction to recommended Scala style, because it * forms a kind of DSL for pending tests. It enables tests in suites such as FunSuite or Spec * to be denoted by placing "(pending)" after the test name, as in: *

* *
   * test("that style rules are not laws") (pending)
   * 
* *

* Readers of the code see "pending" in parentheses, which looks like a little note attached to the test name to indicate * it is pending. Whereas "(pending()) looks more like a method call, "(pending)" lets readers * stay at a higher level, forgetting how it is implemented and just focusing on the intent of the programmer who wrote the code. *

*/ def pending: PendingNothing = { throw new TestPendingException } /** * Execute the passed block of code, and if it completes abruptly, throw TestPendingException, else * throw TestFailedException. * *

* This method can be used to temporarily change a failing test into a pending test in such a way that it will * automatically turn back into a failing test once the problem originally causing the test to fail has been fixed. * At that point, you need only remove the pendingUntilFixed call. In other words, a * pendingUntilFixed surrounding a block of code that isn't broken is treated as a test failure. * The motivation for this behavior is to encourage people to remove pendingUntilFixed calls when * there are no longer needed. *

* *

* This method facilitates a style of testing in which tests are written before the code they test. Sometimes you may * encounter a test failure that requires more functionality than you want to tackle without writing more tests. In this * case you can mark the bit of test code causing the failure with pendingUntilFixed. You can then write more * tests and functionality that eventually will get your production code to a point where the original test won't fail anymore. * At this point the code block marked with pendingUntilFixed will no longer throw an exception (because the * problem has been fixed). This will in turn cause pendingUntilFixed to throw TestFailedException * with a detail message explaining you need to go back and remove the pendingUntilFixed call as the problem orginally * causing your test code to fail has been fixed. *

* * @param f a block of code, which if it completes abruptly, should trigger a TestPendingException * @throws TestPendingException if the passed block of code completes abruptly with an Exception or AssertionError */ def pendingUntilFixed(f: => Unit) { val isPending = try { f false } catch { case _: Exception => true case _: AssertionError => true } if (isPending) throw new TestPendingException else throw new TestFailedException(Resources("pendingUntilFixed"), 2) } /** * The total number of tests that are expected to run when this Suite's run method is invoked. * *

* This trait's implementation of this method returns the sum of: *

* *
    *
  • the size of the testNames List, minus the number of tests marked as ignored *
  • the sum of the values obtained by invoking * expectedTestCount on every nested Suite contained in * nestedSuites *
* * @param filter a Filter with which to filter tests to count based on their tags */ def expectedTestCount(filter: Filter): Int = { // [bv: here was another tricky refactor. How to increment a counter in a loop] def countNestedSuiteTests(nestedSuites: List[Suite], filter: Filter): Int = nestedSuites match { case List() => 0 case nestedSuite :: nestedSuites => nestedSuite.expectedTestCount(filter) + countNestedSuiteTests(nestedSuites, filter) } filter.runnableTestCount(testNames, tags) + countNestedSuiteTests(nestedSuites, filter) } // Wrap any non-DispatchReporter, non-CatchReporter in a CatchReporter, // so that exceptions are caught and transformed // into error messages on the standard error stream. private[scalatest] def wrapReporterIfNecessary(reporter: Reporter) = reporter match { case dr: DispatchReporter => dr case cr: CatchReporter => cr case _ => new CatchReporter(reporter) } } private[scalatest] object Suite { private[scalatest] val TestMethodPrefix = "test" private[scalatest] val InformerInParens = "(Informer)" private[scalatest] val IgnoreAnnotation = "org.scalatest.Ignore" private[scalatest] def getSimpleNameOfAnObjectsClass(o: AnyRef) = stripDollars(parseSimpleName(o.getClass().getName())) // [bv: this is a good example of the expression type refactor. I moved this from SuiteClassNameListCellRenderer] // this will be needed by the GUI classes, etc. private[scalatest] def parseSimpleName(fullyQualifiedName: String) = { val dotPos = fullyQualifiedName.lastIndexOf('.') // [bv: need to check the dotPos != fullyQualifiedName.length] if (dotPos != -1 && dotPos != fullyQualifiedName.length) fullyQualifiedName.substring(dotPos + 1) else fullyQualifiedName } private[scalatest] def checkForPublicNoArgConstructor(clazz: java.lang.Class[_]) = { try { val constructor = clazz.getConstructor(new Array[java.lang.Class[T] forSome { type T }](0): _*) Modifier.isPublic(constructor.getModifiers) } catch { case nsme: NoSuchMethodException => false } } private[scalatest] def stripDollars(s: String): String = { val lastDollarIndex = s.lastIndexOf('$') if (lastDollarIndex < s.length - 1) if (lastDollarIndex == -1 || !s.startsWith("line")) s else s.substring(lastDollarIndex + 1) else { // The last char is a dollar sign val lastNonDollarChar = s.reverse.find(_ != '$') lastNonDollarChar match { case None => s case Some(c) => { val lastNonDollarIndex = s.lastIndexOf(c) if (lastNonDollarIndex == -1) s else stripDollars(s.substring(0, lastNonDollarIndex + 1)) } } } } private[scalatest] def diffStrings(s: String, t: String): Tuple2[String, String] = { def findCommonPrefixLength(s: String, t: String): Int = { val max = s.length.min(t.length) // the maximum potential size of the prefix var i = 0 var found = false while (i < max & !found) { found = (s.charAt(i) != t.charAt(i)) if (!found) i = i + 1 } i } def findCommonSuffixLength(s: String, t: String): Int = { val max = s.length.min(t.length) // the maximum potential size of the suffix var i = 0 var found = false while (i < max & !found) { found = (s.charAt(s.length - 1 - i) != t.charAt(t.length - 1 - i)) if (!found) i = i + 1 } i } val commonPrefixLength = findCommonPrefixLength(s, t) val commonSuffixLength = findCommonSuffixLength(s.substring(commonPrefixLength), t.substring(commonPrefixLength)) val prefix = s.substring(0, commonPrefixLength) val suffix = if (s.length - commonSuffixLength < 0) "" else s.substring(s.length - commonSuffixLength) val sMiddleEnd = s.length - commonSuffixLength val tMiddleEnd = t.length - commonSuffixLength val sMiddle = s.substring(commonPrefixLength, sMiddleEnd) val tMiddle = t.substring(commonPrefixLength, tMiddleEnd) val MaxContext = 20 val shortPrefix = if (commonPrefixLength > MaxContext) "..." + prefix.substring(prefix.length - MaxContext) else prefix val shortSuffix = if (commonSuffixLength > MaxContext) suffix.substring(0, MaxContext) + "..." else suffix (shortPrefix + "[" + sMiddle + "]" + shortSuffix, shortPrefix + "[" + tMiddle + "]" + shortSuffix) } // If the objects are two strings, replace them with whatever is returned by diffStrings. // Otherwise, use the same objects. private[scalatest] def getObjectsForFailureMessage(a: Any, b: Any) = a match { case aStr: String => { b match { case bStr: String => { Suite.diffStrings(aStr, bStr) } case _ => (a, b) } } case _ => (a, b) } private[scalatest] def formatterForSuiteStarting(suite: Suite): Option[Formatter] = suite match { case spec: Spec => Some(IndentedText(suite.suiteName + ":", suite.suiteName, 0)) case spec: FlatSpec => Some(IndentedText(suite.suiteName + ":", suite.suiteName, 0)) case spec: WordSpec => Some(IndentedText(suite.suiteName + ":", suite.suiteName, 0)) case spec: FeatureSpec => Some(IndentedText(suite.suiteName + ":", suite.suiteName, 0)) case _ => None } private[scalatest] def formatterForSuiteCompleted(suite: Suite): Option[Formatter] = suite match { case spec: Spec => Some(MotionToSuppress) case spec: FlatSpec => Some(MotionToSuppress) case spec: WordSpec => Some(MotionToSuppress) case spec: FeatureSpec => Some(MotionToSuppress) case _ => None } private[scalatest] def formatterForSuiteAborted(suite: Suite, message: String): Option[Formatter] = { suite match { case spec: Spec => Some(IndentedText(message, message, 0)) case spec: FlatSpec => Some(IndentedText(message, message, 0)) case spec: WordSpec => Some(IndentedText(message, message, 0)) case spec: FeatureSpec => Some(IndentedText(message, message, 0)) case _ => None } } private def simpleNameForTest(testName: String) = if (testName.endsWith(InformerInParens)) testName.substring(0, testName.length - InformerInParens.length) else testName private[scalatest] def anErrorThatShouldCauseAnAbort(throwable: Throwable) = throwable match { case _: AnnotationFormatError => true case _: AWTError => true case _: CoderMalfunctionError => true case _: FactoryConfigurationError => true case _: LinkageError => true case _: ThreadDeath => true case _: TransformerFactoryConfigurationError => true case _: VirtualMachineError => true case _ => false } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy