ScalaTest 0.9.5
|
|
org/scalatest/FunSuite.scala
]
trait
FunSuite
extends
SuiteFun
” in FunSuite
stands for functional.
Here's an example FunSuite
:
import org.scalatest.FunSuite class MySuite extends FunSuite { test("addition") { val sum = 1 + 1 assert(sum === 2) assert(sum + 2 === 4) } test("subtraction") { val diff = 4 - 1 assert(diff === 3) assert(diff - 2 === 1) } }
“test
” is a method, defined in FunSuite
, which will be invoked
by the primary constructor of MySuite
. You specify the name of the test as
a string between the parentheses, and the test code itself between curly braces.
The test code is a function passed as a by-name parameter to test
, which registers
it for later execution. One benefit of FunSuite
compared to Suite
is you need not name all your
tests starting with “test
.” In addition, you can more easily give long names to
your tests, because you need not encode them in camel case, as you must do
with test methods.
Test fixtures
A test fixture is objects or other artifacts (such as files, sockets, database
connections, etc.) used by tests to do their work. You can use fixtures in
FunSuite
s with the same approaches suggested for Suite
in
its documentation. The same text that appears in the test fixture
section of Suite
's documentation is repeated here, with examples changed from
Suite
to FunSuite
.
If a fixture is used by only one test, then the definitions of the fixture objects should
be local to the test function, such as the objects assigned to sum
and diff
in the
previous MySuite
examples. If multiple tests need to share a fixture, the best 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 functions:
import org.scalatest.FunSuite class MySuite extends FunSuite { // Sharing fixture objects via instance variables val shared = 5 test("Addition") { val sum = 2 + 3 assert(sum === shared) } test("Subtraction") { val diff = 7 - 2 assert(diff === shared) } }
In some cases, however, shared mutable fixture objects may be changed by test methods such that
it needs to be recreated or reinitialized before each test. Shared resources such
as files or database connections may also need to
be cleaned up after each test. JUnit offers methods setup
and
tearDown
for this purpose. In ScalaTest, you can use the BeforeAndAfter
trait,
which will be described later, to implement an approach similar to JUnit's setup
and tearDown
, however, this approach often involves reassigning var
s
between tests. Before going that route, you should consider two approaches that
avoid var
s. One approach is to write one or more "create" methods
that return a new instance of a needed object (or a tuple of new instances of
multiple objects) each time it is called. You can then call a create method at the beginning of each
test that needs the fixture, storing the fixture object or objects in local variables. Here's an example:
import org.scalatest.FunSuite import scala.collection.mutable.ListBuffer class MySuite extends FunSuite { // create objects needed by tests and return as a tuple def createFixture = ( new StringBuilder("ScalaTest is "), new ListBuffer[String] ) test("Easy") { val (builder, lbuf) = createFixture builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(lbuf.isEmpty) lbuf += "sweet" } test("Fun") { val (builder, lbuf) = createFixture builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(lbuf.isEmpty) } }
Another approach to mutable fixture objects that avoids var
s is to create "with" methods,
which take test code as a function that takes the fixture objects as parameters, and wrap test code in calls to the "with" method. Here's an example:
import org.scalatest.FunSuite import scala.collection.mutable.ListBuffer class MySuite extends FunSuite { def withFixture(testFunction: (StringBuilder, ListBuffer[String]) => Unit) { // Create needed mutable objects val sb = new StringBuilder("ScalaTest is ") val lb = new ListBuffer[String] // Invoke the test function, passing in the mutable objects testFunction(sb, lb) } test("Easy") { withFixture { (builder, lbuf) => { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(lbuf.isEmpty) lbuf += "sweet" } } } test("Fun") { withFixture { (builder, lbuf) => { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(lbuf.isEmpty) } } } }One advantage of this approach compared to the create method approach shown previously is that you can more easily perform cleanup after each test executes. For example, you could create a temporary file before each test, and delete it afterwords, by doing so before and after invoking the test function in a
withTempFile
method. Here's an example:
import org.scalatest.FunSuite import java.io.FileReader import java.io.FileWriter import java.io.File class MySuite extends FunSuite { def withTempFile(testFunction: FileReader => Unit) { 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 using the temp file testFunction(reader) } finally { // Close and delete the temp file reader.close() val file = new File(FileName) file.delete() } } test("Reading from the temp file") { withTempFile { (reader) => { var builder = new StringBuilder var c = reader.read() while (c != -1) { builder.append(c.toChar) c = reader.read() } assert(builder.toString === "Hello, test!") } } } test("First char of the temp file") { withTempFile { (reader) => { assert(reader.read() === 'H') } } } }
If you are more comfortable with reassigning instance variables, however, you can
instead use the BeforeAndafter
trait, which provides
methods that will be run before and after each test. BeforeAndAfter
's
beforeEach
method will be run before, and its afterEach
method after, each test (like JUnit's setup
and tearDown
methods, respectively). For example, here's how you'd write the previous
test that uses a temp file with BeforeAndAfter
:
import org.scalatest.FunSuite import org.scalatest.BeforeAndAfter import java.io.FileReader import java.io.FileWriter import java.io.File class MySuite extends FunSuite with BeforeAndAfter { 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() } test("Reading from the temp file") { var builder = new StringBuilder var c = reader.read() while (c != -1) { builder.append(c.toChar) c = reader.read() } assert(builder.toString === "Hello, test!") } test("First char of the temp file") { assert(reader.read() === 'H') } }
In this example, the instance variable reader
is a var
, so
it can be reinitialized between tests by the beforeEach
method. 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 BeforeAndAfter
. See the documentation for BeforeAndAfter
for
an example.
Test groups
A FunSuite
's tests may be classified into named groups.
As with any suite, when executing a FunSuite
, groups of tests can
optionally be included and/or excluded. To place FunSuite
tests into
groups, you pass objects that extend abstract class org.scalatest.Group
to methods
that register tests. Class Group
takes one parameter, a string name. If you have
created Java annotation interfaces for use as group names in direct subclasses of org.scalatest.Suite
,
then you will probably want to use group names on your FunSuite
s that match. To do so, simply
pass the fully qualified names of the Java interfaces to the Group
constructor. For example, if you've
defined Java annotation interfaces with fully qualified names, com.mycompany.groups.SlowTest
and com.mycompany.groups.DBTest
, then you could
create matching groups for FunSuite
s like this:
import org.scalatest.Group object SlowTest extends Group("com.mycompany.groups.SlowTest") object DBTest extends Group("com.mycompany.groups.DBTest")
Given these definitions, you could place FunSuite
tests into groups like this:
import org.scalatest.FunSuite class MySuite extends FunSuite { test("addition", SlowTest) { val sum = 1 + 1 assert(sum === 2) assert(sum + 2 === 4) } test("subtraction", SlowTest, DBTest) { val diff = 4 - 1 assert(diff === 3) assert(diff - 2 === 1) } }
This code places both tests, "addition" and "subtraction," into the com.mycompany.groups.SlowTest
group,
and test "subtraction" into the com.mycompany.groups.DBTest
group.
The primary execute method takes two Set[String]
s called groupsToInclude
and
groupsToExclude
. If groupsToInclude
is empty, all tests will be executed
except those those belonging to groups listed in the
groupsToExclude
Set
. If groupsToInclude
is non-empty, only tests
belonging to groups mentioned in groupsToInclude
, and not mentioned in groupsToExclude
,
will be executed.
Ignored tests
To support the common use case of “temporarily” disabling a test, with the
good intention of resurrecting the test at a later time, FunSuite
provides registration
methods that start with ignore
instead of test
. For example, to temporarily
disable the test named addition
, just change “test
” into “ignore
,” like this:
import org.scalatest.FunSuite class MySuite extends FunSuite { ignore("addition") { val sum = 1 + 1 assert(sum === 2) assert(sum + 2 === 4) } test("subtraction") { val diff = 4 - 1 assert(diff === 3) assert(diff - 2 === 1) } }
If you run this version of MySuite
with:
scala> (new MySuite).execute()
It will run only subtraction
and report that addition
was ignored:
Test Ignored - MySuite: addition Test Starting - MySuite: subtraction Test Succeeded - MySuite: subtraction
As with org.scalatest.Suite
, the ignore feature is implemented as a group. The execute
method that takes no parameters
adds org.scalatest.Ignore
to the groupsToExclude
Set
it passes to
the primary execute
method, as does Runner
. The only difference between
org.scalatest.Ignore
and the groups 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.
Informers
One of the parameters to the primary execute
method is a 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 FunSuite
's methods will be sufficient, but
occasionally you may wish to provide custom information to the Reporter
from a test.
For this purpose, an Informer
that will forward information to the current Reporter
is provided via the info
parameterless method.
You can pass the extra information to the Informer
via one of its apply
methods.
The Informer
will then pass the information to the Reporter
's infoProvided
method.
Here's an example:
import org.scalatest.FunSuite class MySuite extends FunSuite { test("addition") { val sum = 1 + 1 assert(sum === 2) assert(sum + 2 === 4) info("Addition seems to work") } }If you run this
Suite
from the interpreter, you will see the following message
included in the printed report:
Test Starting - MySuite: addition Info Provided - MySuite.addition: Addition seems to work Test Succeeded - MySuite: addition
Method Summary | |
override def
|
execute
(testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any], distributor : scala.Option[Distributor]) : Unit
Execute this
Suite . |
override def
|
groups
: scala.collection.immutable.Map[java.lang.String, scala.collection.immutable.Set[java.lang.String]]
A
Map whose keys are String group names to which tests in this FunSuite belong, and values
the Set of test names that belong to each group. If this FunSuite contains no groups, this method returns an empty Map . |
protected def
|
ignore
(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test to ignore, which has the specified name, optional groups, and function value that takes no arguments.
This method will register the test for later ignoring via an invocation of one of the
execute
methods. This method exists to make it easy to ignore an existing test method by changing the call to test
to ignore without deleting or commenting out the actual test code. The test will not be executed, but a
report will be sent that indicates the test was ignored. The passed test name must not have been registered previously on
this FunSuite instance. |
implicit def
|
info
: Informer
Returns an
Informer that during test execution will forward strings (or reports) passed to its
apply method to the current reporter. If invoked inside a test function, it will forward the information to
the current reporter immediately. If invoked outside a test function, but in the primary constructor, it
will register the info for forwarding later during test execution. If invoked at any other time, it will
throw an exception. |
protected override def
|
runTest
(testName : java.lang.String, reporter : Reporter, stopper : Stopper, goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
Run a test. This trait's implementation runs the test registered with the name specified by
testName . |
protected override def
|
runTests (testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit |
protected def
|
test
(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
Register a test with the specified name, optional groups, and function value that takes no arguments.
This method will register the test for later execution via an invocation of one of the
execute
methods. The passed test name must not have been registered previously on
this FunSuite instance. |
override def
|
testNames
: scala.collection.immutable.Set[java.lang.String]
An immutable
Set of test names. If this FunSuite contains no tests, this method returns an empty Set . |
Methods inherited from Suite | |
nestedSuites, execute, execute, runNestedSuites, suiteName, expectedTestCount |
Methods inherited from Assertions | |
assert, assert, assert, assert, convertToEqualizer, intercept, intercept, intercept, expect, expect, fail, fail, fail, fail |
Methods inherited from AnyRef | |
getClass, hashCode, equals, clone, toString, notify, notifyAll, wait, wait, wait, finalize, ==, !=, eq, ne, synchronized |
Methods inherited from Any | |
==, !=, isInstanceOf, asInstanceOf |
Method Details |
implicit
def
info : Informer
Informer
that during test execution will forward strings (or reports) passed to its
apply method to the current reporter. If invoked inside a test function, it will forward the information to
the current reporter immediately. If invoked outside a test function, but in the primary constructor, it
will register the info for forwarding later during test execution. If invoked at any other time, it will
throw an exception.protected
def
test(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
execute
methods. The passed test name must not have been registered previously on
this FunSuite
instance.TestFailedException -
if testName
had been registered previouslyprotected
def
ignore(testName : java.lang.String, testGroups : Group*)(f : => Unit) : Unit
execute
methods. This method exists to make it easy to ignore an existing test method by changing the call to test
to ignore
without deleting or commenting out the actual test code. The test will not be executed, but a
report will be sent that indicates the test was ignored. The passed test name must not have been registered previously on
this FunSuite
instance.TestFailedException -
if testName
had been registered previouslyoverride
def
testNames : scala.collection.immutable.Set[java.lang.String]
Set
of test names. If this FunSuite
contains no tests, this method returns an empty Set
.
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.
protected override
def
runTest(testName : java.lang.String, reporter : Reporter, stopper : Stopper, goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
testName
.testName -
the name of one test to execute.reporter -
the Reporter
to which results will be reportedstopper -
the Stopper
that will be consulted to determine whether to stop execution early.goodies -
a Map
of properties that can be used by the executing Suite
of tests.NullPointerException -
if any of testName
, reporter
, stopper
, or goodies
is null
.override
def
groups : scala.collection.immutable.Map[java.lang.String, scala.collection.immutable.Set[java.lang.String]]
Map
whose keys are String
group names to which tests in this FunSuite
belong, and values
the Set
of test names that belong to each group. If this FunSuite
contains no groups, this method returns an empty Map
.
This trait's implementation returns groups that were passed as strings contained in Group
objects passed to
methods test
and ignore
.
protected override
def
runTests(testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any]) : Unit
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 Some
, 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 methodreporter
- the Reporter
passed to this method, or one that wraps and delegates to itstopper
- the Stopper
passed to this method, or one that wraps and delegates to itgoodies
- the goodies
Map
passed to this method, or one that wraps and delegates to it
This method takes a Set
of group names that should be included (groupsToInclude
), and a Set
that should be excluded (groupsToExclude
), when deciding which of this Suite
's tests to execute.
If groupsToInclude
is empty, all tests will be executed
except those those belonging to groups listed in the groupsToExclude
Set
. If groupsToInclude
is non-empty, only tests
belonging to groups mentioned in groupsToInclude
, and not mentioned in groupsToExclude
will be executed. However, if testName
is Some
, groupsToInclude
and groupsToExclude
are essentially ignored.
Only if testName
is None
will groupsToInclude
and groupsToExclude
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 trait groups, see the main documentation for this trait.
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 execute.
(A testNames
value of None
essentially acts as a wildcard that means all tests in
this Suite
that are selected by groupsToInclude
and groupsToExclude
should be executed.)
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 groupsToInclude
and groupsToExclude
Set
s.
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 itstopper
- the Stopper
passed to this method, or one that wraps and delegates to itgoodies
- the goodies
Map
passed to this method, or one that wraps and delegates to ittestName -
an optional name of one test to execute. If None
, all relevant tests should be executed. I.e., None
acts like a wildcard that means execute all relevant tests in this Suite
.reporter -
the Reporter
to which results will be reportedstopper -
the Stopper
that will be consulted to determine whether to stop execution early.groupsToInclude -
a Set
of String
group names to include in the execution of this Suite
groupsToExclude -
a Set
of String
group names to exclude in the execution of this Suite
goodies -
a Map
of key-value pairs that can be used by the executing Suite
of tests.NullPointerException -
if any of testName
, reporter
, stopper
, groupsToInclude
, groupsToExclude
, or goodies
is null
.
This trait's implementation of this method executes tests
in the manner described in detail in the following paragraphs, but subclasses may override the method to provide different
behavior. The most common reason to override this method is to set up and, if also necessary, to clean up a test fixture
used by all the methods of this Suite
.override
def
execute(testName : scala.Option[java.lang.String], reporter : Reporter, stopper : Stopper, groupsToInclude : scala.collection.immutable.Set[java.lang.String], groupsToExclude : scala.collection.immutable.Set[java.lang.String], goodies : scala.collection.immutable.Map[java.lang.String, Any], distributor : scala.Option[Distributor]) : Unit
Suite
.
If testName
is None
, this trait's implementation of this method
calls these two methods on this object in this order:
runNestedSuites(wrappedReporter, stopper, groupsToInclude, groupsToExclude, goodies, distributor)
runTests(testName, wrappedReporter, stopper, groupsToInclude, groupsToExclude, goodies)
If testName
is Some
, then this trait's implementation of this method
calls runTests
, but does not call runNestedSuites
.
testName -
an optional name of one test to execute. If None
, all relevant tests should be executed. I.e., None
acts like a wildcard that means execute all relevant tests in this Suite
.reporter -
the Reporter
to which results will be reportedstopper -
the Stopper
that will be consulted to determine whether to stop execution early.groupsToInclude -
a Set
of String
group names to include in the execution of this Suite
groupsToExclude -
a Set
of String
group names to exclude in the execution of this Suite
goodies -
a Map
of key-value pairs that can be used by the executing Suite
of tests.distributor -
an optional Distributor
, into which to put nested Suite
s to be executed by another entity, such as concurrently by a pool of threads. If None
, nested Suite
s will be executed sequentially.NullPointerException -
if any passed parameter is null
.
ScalaTest 0.9.5
|
|