package scalatest
ScalaTest's main traits, classes, and other members, including members supporting ScalaTest's DSL for the Scala interpreter.
- Source
- package.scala
- Alphabetic
- By Inheritance
- scalatest
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Type Members
-
trait
Alerter
extends AnyRef
Trait providing an
apply
method to which alert messages about a running suite of tests can be reported.Trait providing an
apply
method to which alert messages about a running suite of tests can be reported.An
Alerter
is essentially used to wrap aReporter
and provide easy ways to send alert messages to thatReporter
via anAlertProvided
event.Alerter
contains anapply
method that takes a string and an optional payload object of typeAny
. TheAlerter
will forward the passed alertmessage
string to theReporter
as themessage
parameter, and the optional payload object as thepayload
parameter, of anAlertProvided
event.For insight into the differences between
Alerter
,Notifier
, andInformer
, see the main documentation for traitAlerting
. -
trait
Alerting
extends AnyRef
Trait that contains the
alert
method, which can be used to send an alert to the reporter.Trait that contains the
alert
method, which can be used to send an alert to the reporter.One difference between
alert
and theinfo
method ofInformer
is thatinfo
messages provided during a test are recorded and sent as part of test completion event, whereasalert
messages are sent right away asAlertProvided
messages. For long-running tests,alert
allows you to send "alert notifications" to the reporter right away, so users can be made aware of potential problems being experienced by long-running tests. By contrast,info
messages will only be seen by the user after the test has completed, and are more geared towards specification (such as Given/When/Then messages) than notification.The difference between
alert
and theupdate
method ofUpdating
is thatalert
is intended to be used for warnings or notifications of potential problems, whereasupdate
is just for status updates. In string reporters for which ANSI color is enabled,update
notifications are shown in green andalert
notifications in yellow. -
trait
AppendedClues
extends AnyRef
Trait providing an implicit conversion that allows clues to be placed after a block of code.
Trait providing an implicit conversion that allows clues to be placed after a block of code.
You can use the
withClue
construct provided byAssertions
, which is extended by every style trait in ScalaTest, to add extra information to reports of failed or canceled tests. ThewithClue
fromAssertions
places the "clue string" at the front, both in the code and in the resulting message:withClue("This is a prepended clue;") { 1 + 1 should equal (3) }
The above expression will yield the failure message:
This is a prepended clue; 2 did not equal 3
If you mix in this trait, or import its members via its companion object, you can alternatively place the clue string at the end, like this:
{ 1 + 1 should equal (3) } withClue "now the clue comes after"
The above expression will yield the failure message:
2 did not equal 3 now the clue comes after
If no space is already present, either at the beginning of the clue string or at the end of the current message, a space will be placed between the two, unless the clue string starts with one of the punctuation characters: comma (
,
), period (.
), or semicolon (;
). For example, the failure message in the above example includes an extra space inserted between 3 and now.By contrast this code, which has a clue string starting with comma:
{ 1 + 1 should equal (3) } withClue ", now the clue comes after"
Will yield a failure message with no extra inserted space:
2 did not equal 3, now the clue comes after
The
withClue
method will only append the clue string to the detail message of exception types that mix in theModifiableMessage
trait. See the documentation forModifiableMessage
for more information.Note: the reason this functionality is not provided by
Assertions
directly, like the prependedwithClue
construct, is because appended clues require an implicit conversion. ScalaTest only gives you one implicit conversion by default in any test class to minimize the potential for conflicts with other implicit conversions you may be using. All other implicit conversions, including the one provided by this trait, you must explicitly invite into your code through inheritance or an import. -
case class
Args
(reporter: Reporter, stopper: Stopper = Stopper.default, filter: Filter = Filter.default, configMap: ConfigMap = ConfigMap.empty, distributor: Option[Distributor] = None, tracker: Tracker = Tracker.default, chosenStyles: Set[String] = Set.empty, runTestInNewInstance: Boolean = false, distributedTestSorter: Option[DistributedTestSorter] = None, distributedSuiteSorter: Option[DistributedSuiteSorter] = None) extends Product with Serializable
Arguments bundle passed to four of ScalaTest's lifecycle methods:
run
,runNestedSuites
,runTests
, andrunTest
.Arguments bundle passed to four of ScalaTest's lifecycle methods:
run
,runNestedSuites
,runTests
, andrunTest
.The signatures of these methods, defined in trait
Suite
, are:def run(testName: Option[String], args: Args) def runNestedSuites(args: Args) def runTests(testName: Option[String], args: Args) def runTest(testName: String, args: Args)
The purpose of bundling these arguments into an
Args
object instead of passing them in individually is to make the signature of these four lifecycle methods easier to read, write, and remember, as well as to make the methods more pleasant to override in user code.- reporter
the
Reporter
to which results will be reported- stopper
the
Stopper
that will be consulted to determine whether to stop execution early.- filter
a
Filter
with which to filter tests based on their tags- configMap
a
ConfigMap
of key-value pairs that can be used by the executingSuite
of tests.- distributor
an optional
Distributor
, into which to put nestedSuite
s to be executed by another entity, such as concurrently by a pool of threads. IfNone
, nestedSuite
s will be executed sequentially.- tracker
a
Tracker
trackingOrdinal
s being fired by the current thread.- chosenStyles
a (possibly empty)
Set
ofString
s specifying the run's chosen styles- runTestInNewInstance
a flag used to pass information between run methods in
OneInstancePerTest
andParallelTestExecution
.- distributedTestSorter
an optional
DistributedTestSorter
used byParallelTestExecution
to sort the events for the parallel-executed tests of one suite back into sequential order on the fly, with a timeout in case a test takes too long to complete- distributedSuiteSorter
an optional
DistributedSuiteSorter
used byParallelTestExecution
to ensure the events for the parallel-executed suites are sorted back into sequential order, with a timeout in case a suite takes to long to complete, even when tests are executed in parallel
- Exceptions thrown
NullArgumentException
if any passed parameter isnull
.
- type Assertion = scalatest.compatible.Assertion
-
trait
Assertions
extends TripleEquals
Trait that contains ScalaTest's basic assertion methods.
Trait that contains ScalaTest's basic assertion methods.
You can use the assertions provided by this trait in any ScalaTest
Suite
, becauseSuite
mixes in this trait. This trait is designed to be used independently of anything else in ScalaTest, though, so you can mix it into anything. (You can alternatively import the methods defined in this trait. For details, see the documentation for theAssertions
companion object.In any Scala program, you can write assertions by invoking
assert
and passing in aBoolean
expression, such as:val left = 2 val right = 1 assert(left == right)
If the passed expression is
true
,assert
will return normally. Iffalse
, Scala'sassert
will complete abruptly with anAssertionError
. This behavior is provided by theassert
method defined in objectPredef
, whose members are implicitly imported into every Scala source file. ThisAssertions
trait defines anotherassert
method that hides the one inPredef
. It behaves the same, except that iffalse
is passed it throwsTestFailedException
instead ofAssertionError
. Why? Because unlikeAssertionError
,TestFailedException
carries information about exactly which item in the stack trace represents the line of test code that failed, which can help users more quickly find an offending line of code in a failing test. In addition, ScalaTest'sassert
provides better error messages than Scala'sassert
.If you pass the previous
Boolean
expression,left == right
toassert
in a ScalaTest test, a failure will be reported that, becauseassert
is implemented as a macro, includes reporting the left and right values. For example, given the same code as above but using ScalaTest assertions:import org.scalatest.Assertions._ val left = 2 val right = 1 assert(left == right)
The detail message in the thrown
TestFailedException
from thisassert
will be: "2 did not equal 1".ScalaTest's
assert
macro works by recognizing patterns in the AST of the expression passed toassert
and, for a finite set of common expressions, giving an error message that an equivalent ScalaTest matcher expression would give. Here are some examples, wherea
is 1,b
is 2,c
is 3,d
is 4,xs
isList(a, b, c)
, andnum
is 1.0:assert(a == b || c >= d) // Error message: 1 did not equal 2, and 3 was not greater than or equal to 4 assert(xs.exists(_ == 4)) // Error message: List(1, 2, 3) did not contain 4 assert("hello".startsWith("h") && "goodbye".endsWith("y")) // Error message: "hello" started with "h", but "goodbye" did not end with "y" assert(num.isInstanceOf[Int]) // Error message: 1.0 was not instance of scala.Int assert(Some(2).isEmpty) // Error message: Some(2) was not empty
For expressions that are not recognized, the macro currently prints out a string representation of the (desugared) AST and adds
"was false"
. Here are some examples of error messages for unrecognized expressions:assert(None.isDefined) // Error message: scala.None.isDefined was false assert(xs.exists(i => i > 10)) // Error message: xs.exists(((i: Int) => i.>(10))) was false
You can augment the standard error message by providing a
String
as a second argument toassert
, like this:val attempted = 2 assert(attempted == 1, "Execution was attempted " + left + " times instead of 1 time")
Using this form of
assert
, the failure report will be more specific to your problem domain, thereby helping you debug the problem. ThisAssertions
trait also mixes in theTripleEquals
, which gives you a===
operator that allows you to customizeEquality
, perform equality checks with numericTolerance
, and enforce type constraints at compile time with sibling traitsTypeCheckedTripleEquals
andConversionCheckedTripleEquals
.Expected results
Although the
assert
macro provides a natural, readable extension to Scala'sassert
mechanism that provides good error messages, as the operands become lengthy, the code becomes less readable. In addition, the error messages generated for==
and===
comparisons don't distinguish between actual and expected values. The operands are just calledleft
andright
, because if one were namedexpected
and the otheractual
, it would be difficult for people to remember which was which. To help with these limitations of assertions,Suite
includes a method calledassertResult
that can be used as an alternative toassert
. To useassertResult
, you place the expected value in parentheses afterassertResult
, followed by curly braces containing code that should result in the expected value. For example:val a = 5 val b = 2 assertResult(2) { a - b }
In this case, the expected value is
2
, and the code being tested isa - b
. This assertion will fail, and the detail message in theTestFailedException
will read, "Expected 2, but got 3."Forcing failures
If you just need the test to fail, you can write:
fail()
Or, if you want the test to fail with a message, write:
fail("I've got a bad feeling about this")
Achieving success
In async style tests, you must end your test body with either
Future[Assertion]
orAssertion
. ScalaTest's assertions (including matcher expressions) have result typeAssertion
, so ending with an assertion will satisfy the compiler. If a test body or function body passed toFuture.map
does not end with typeAssertion
, however, you can fix the type error by placingsucceed
at the end of the test or function body:succeed // Has type Assertion
Expected 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 3 style, like this:
val s = "hi" try { s.charAt(-1) fail() } catch { case _: IndexOutOfBoundsException => // Expected, so continue }
If
charAt
throwsIndexOutOfBoundsException
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 run. Thefail
method always completes abruptly with aTestFailedException
, thereby signaling a failed test.To make this common use case easier to express and read, ScalaTest provides two methods:
assertThrows
andintercept
. Here's how you useassertThrows
:val s = "hi" assertThrows[IndexOutOfBoundsException] { // Result type: Assertion s.charAt(-1) }
This code behaves much like the previous example. If
charAt
throws an instance ofIndexOutOfBoundsException
,assertThrows
will returnSucceeded
. But ifcharAt
completes normally, or throws a different exception,assertThrows
will complete abruptly with aTestFailedException
.The
intercept
method behaves the same asassertThrows
, except that instead of returningSucceeded
,intercept
returns the caught exception so that you can inspect it further if you wish. For example, you may need to ensure that data contained inside the exception have expected values. Here's an example:val s = "hi" val caught = intercept[IndexOutOfBoundsException] { // Result type: IndexOutOfBoundsException s.charAt(-1) } assert(caught.getMessage.indexOf("-1") != -1)
Checking that a snippet of code does or does not compile
Often when creating libraries you may wish to ensure that certain arrangements of code that represent potential “user errors” do not compile, so that your library is more error resistant. ScalaTest's
Assertions
trait includes the following syntax for that purpose:assertDoesNotCompile("val a: String = 1")
If you want to ensure that a snippet of code does not compile because of a type error (as opposed to a syntax error), use:
assertTypeError("val a: String = 1")
Note that the
assertTypeError
call will only succeed if the given snippet of code does not compile because of a type error. A syntax error will still result on a thrownTestFailedException
.If you want to state that a snippet of code does compile, you can make that more obvious with:
assertCompiles("val a: Int = 1")
Although the previous three constructs are implemented with macros that determine at compile time whether the snippet of code represented by the string does or does not compile, errors are reported as test failures at runtime.
Assumptions
Trait
Assertions
also provides methods that allow you to cancel a test. You would cancel a test if a resource required by the test was unavailable. For example, if a test requires an external database to be online, and it isn't, the test could be canceled to indicate it was unable to run because of the missing database. Such a test assumes a database is available, and you can use theassume
method to indicate this at the beginning of the test, like this:assume(database.isAvailable)
For each overloaded
assert
method, traitAssertions
provides an overloadedassume
method with an identical signature and behavior, except theassume
methods throwTestCanceledException
whereas theassert
methods throwTestFailedException
. As withassert
,assume
hides a Scala method inPredef
that performs a similar function, but throwsAssertionError
. And just as you can withassert
, you will get an error message extracted by a macro from the AST passed toassume
, and can optionally provide a clue string to augment this error message. Here are some examples:assume(database.isAvailable, "The database was down again") assume(database.getAllUsers.count === 9)
Forcing cancelations
For each overloaded
fail
method, there's a correspondingcancel
method with an identical signature and behavior, except thecancel
methods throwTestCanceledException
whereas thefail
methods throwTestFailedException
. Thus if you just need to cancel a test, you can write:cancel()
If you want to cancel the test with a message, just place the message in the parentheses:
cancel("Can't run the test because no internet connection was found")
Getting a clue
If you want more information that is provided by default by the methods if this trait, you can supply a "clue" string in one of several ways. The extra information (or "clues") you provide will be included in the detail message of the thrown exception. Both
assert
andassertResult
provide a way for a clue to be included directly,intercept
does not. Here's an example of clues provided directly inassert
:assert(1 + 1 === 3, "this is a clue")
and in
assertResult
:assertResult(3, "this is a clue") { 1 + 1 }
The exceptions thrown by the previous two statements will include the clue string,
"this is a clue"
, in the exception's detail message. To get the same clue in the detail message of an exception thrown by a failedintercept
call requires usingwithClue
:withClue("this is a clue") { intercept[IndexOutOfBoundsException] { "hi".charAt(-1) } }
The
withClue
method will only prepend the clue string to the detail message of exception types that mix in theModifiableMessage
trait. See the documentation forModifiableMessage
for more information. If you wish to place a clue string after a block of code, see the documentation forAppendedClues
.Note: ScalaTest's
assertTypeError
construct is in part inspired by theillTyped
macro of shapeless. -
abstract
class
AsyncFeatureSpec
extends AsyncFeatureSpecLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
FeatureSpec
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
FeatureSpec
tests.Recommended Usage: AsyncFeatureSpec
is intended to enable users ofFeatureSpec
to write non-blocking asynchronous tests that are consistent with their traditionalFeatureSpec
tests. Note:AsyncFeatureSpec
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFeatureSpec
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Although not required,
FeatureSpec
is often used together withGivenWhenThen
to express acceptance requirements in more detail. Here's an exampleAsyncFeatureSpec
:package org.scalatest.examples.asyncfeaturespec import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages case object IsOn case object PressPowerButton class TVSetActor { // Simulating an actor private var on: Boolean = false def !(msg: PressPowerButton.type): Unit = synchronized { on = !on } def ?(msg: IsOn.type)(implicit c: ExecutionContext): Future[Boolean] = Future { synchronized { on } } } class TVSetActorSpec extends AsyncFeatureSpec with GivenWhenThen { implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global info("As a TV set owner") info("I want to be able to turn the TV on and off") info("So I can watch TV when I want") info("And save energy when I'm not watching TV") feature("TV power button") { scenario("User presses power button when TV is off") { Given("a TV set that is switched off") val tvSetActor = new TVSetActor When("the power button is pressed") tvSetActor ! PressPowerButton Then("the TV should switch on") val futureBoolean = tvSetActor ? IsOn futureBoolean map { isOn => assert(isOn) } } scenario("User presses power button when TV is on") { Given("a TV set that is switched on") val tvSetActor = new TVSetActor tvSetActor ! PressPowerButton When("the power button is pressed") tvSetActor ! PressPowerButton Then("the TV should switch off") val futureBoolean = tvSetActor ? IsOn futureBoolean map { isOn => assert(!isOn) } } } }
Note: for more information on the calls to
Given
,When
, andThen
, see the documentation for traitGivenWhenThen
and theInformers
section below.An
AsyncFeatureSpec
contains feature clauses and scenarios. You define a feature clause withfeature
, and a scenario withscenario
. Bothfeature
andscenario
are methods, defined inAsyncFeatureSpec
, which will be invoked by the primary constructor ofTVSetActorSpec
. A feature clause describes a feature of the subject (class or other entity) you are specifying and testing. In the previous example, the subject under specification and test is a TV set. The feature being specified and tested is the behavior of a TV set when its power button is pressed. With each scenario you provide a string (the spec text) that specifies the behavior of the subject for one scenario in which the feature may be used, and a block of code that tests that behavior. You place the spec text between the parentheses, followed by the test code between curly braces. The test code will be wrapped up as a function passed as a by-name parameter toscenario
, which will register the test for later execution. The result type of the by-name in anAsyncFeatureSpec
must beFuture[Assertion]
.Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. When anAsyncFeatureSpec
is constructed, any test that results inAssertion
will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
only, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker.An
AsyncFeatureSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Scenarios can only be registered with the
scenario
method while theAsyncFeatureSpec
is in its registration phase. Any attempt to register a scenario after theAsyncFeatureSpec
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncFeatureSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncFeatureSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Each scenario represents one test. The name of the test is the spec text passed to the
scenario
method. The feature name does not appear as part of the test name. In aAsyncFeatureSpec
, therefore, you must take care to ensure that each test has a unique name (in other words, that eachscenario
has unique spec text).When you run a
AsyncFeatureSpec
, it will sendFormatter
s in the events it sends to theReporter
. ScalaTest's built-in reporters will report these events in such a way that the output is easy to read as an informal specification of the subject being tested. For example, were you to runTVSetSpec
from within the Scala interpreter:scala> org.scalatest.run(new TVSetActorSpec)
You would see:
TVSetActorSpec: As a TV set owner I want to be able to turn the TV on and off So I can watch TV when I want And save energy when I'm not watching TV Feature: TV power button Scenario: User presses power button when TV is off Given a TV set that is switched off When the power button is pressed Then the TV should switch on Scenario: User presses power button when TV is on Given a TV set that is switched on When the power button is pressed Then the TV should switch off
Or, to run just the “
Feature: TV power button Scenario: User presses power button when TV is on
” method, you could pass that test's name, or any unique substring of the name, such as"TV is on"
. Here's an example:scala> org.scalatest.run(new TVSetActorSpec, "TV is on") TVSetActorSpec: As a TV set owner I want to be able to turn the TV on and off So I can watch TV when I want And save energy when I'm not watching TV Feature: TV power button Scenario: User presses power button when TV is on Given a TV set that is switched on When the power button is pressed Then the TV should switch off
Asynchronous execution model
AsyncFeatureSpec
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncFeatureSpec
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
FeatureSpec
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncFeatureSpec
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncFeatureSpec
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asFeatureSpec
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncFeatureSpec
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncFeatureSpec
provides registration methods that start withignore
instead ofscenario
. Here's an example:package org.scalatest.examples.asyncfeaturespec.ignore import org.scalatest.AsyncFeatureSpec import scala.concurrent.Future class AddSpec extends AsyncFeatureSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } def addNow(addends: Int*): Int = addends.sum feature("The add methods") { ignore("addSoon will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } scenario("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
If you run class
AddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run only the second test and report that the first test was ignored:
AddSpec: Feature: The add methods - Scenario: addSoon will eventually compute a sum of passed Ints !!! IGNORED !!! - Scenario: addNow will immediately compute a sum of passed Ints
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncfeaturespec.ignoreall import org.scalatest.AsyncFeatureSpec import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSpec extends AsyncFeatureSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } def addNow(addends: Int*): Int = addends.sum feature("The add methods") { scenario("addSoon will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } scenario("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSpec
in the Scala interpreter, you'll see:AddSpec: Feature: The add methods - Scenario: addSoon will eventually compute a sum of passed Ints !!! IGNORED !!! - Scenario: addNow will immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
it
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncFeatureSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the default reporting done byAsyncFeatureSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a scenario to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byAsyncFeatureSpec
to pass such information to the reporter. You can see this in action in the initial example of this trait's documentation.Documenters
AsyncFeatureSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FlatSpec
that usesmarkup
:package org.scalatest.examples.asyncfeaturespec.markup import collection.mutable import org.scalatest._ class SetSpec extends AsyncFeatureSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } feature("An element can be added to an empty mutable Set") { scenario("When an element is added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncfeaturespec.note import collection.mutable import org.scalatest._ class SetSpec extends AsyncFeatureSpec { feature("An element can be added to an empty mutable Set") { scenario("When an element is added to an empty mutable Set") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: Feature: An element can be added to an empty mutable Set + notes are sent immediately + alerts are also sent immediately Scenario: When an element is added to an empty mutable Set info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncfeaturespec.pending import org.scalatest.AsyncFeatureSpec import scala.concurrent.Future class AddSpec extends AsyncFeatureSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } def addNow(addends: Int*): Int = addends.sum feature("The add methods") { scenario("addSoon will eventually compute a sum of passed Ints") (pending) scenario("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run both tests, but report that first test is pending. You'll see:
AddSpec: Feature: The add methods - Scenario: addSoon will eventually compute a sum of passed Ints (pending) - Scenario: addNow will immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFeatureSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFeatureSpec
, groups of tests can optionally be included and/or excluded. To tag anAsyncFeatureSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFeatureSpec
s like this:package org.scalatest.examples.asyncfeaturespec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFeatureSpec
tests into groups with tags like this:import org.scalatest.AsyncFeatureSpec import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSpec extends AsyncFeatureSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } def addNow(addends: Int*): Int = addends.sum feature("The add methods") { scenario("addSoon will eventually compute a sum of passed Ints", Slow) { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } scenario("addNow will immediately compute a sum of passed Ints", Slow, DbTest) { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFeatureSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncfeaturespec.getfixture import org.scalatest.AsyncFeatureSpec import scala.concurrent.Future class ExampleSpec extends AsyncFeatureSpec { def fixture: Future[String] = Future { "ScalaTest is designed to " } feature("Simplicity") { scenario("User needs to read test code written by others") { val future = fixture val result = future map { s => s + "encourage clear code!" } result map { s => assert(s == "ScalaTest is designed to encourage clear code!") } } scenario("User needs to understand what the tests are doing") { val future = fixture val result = future map { s => s + "be easy to reason about!" } result map { s => assert(s == "ScalaTest is designed to be easy to reason about!") } } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFeatureSpec
.Trait
AsyncFeatureSpec
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncfeaturespec.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSpec extends AsyncFeatureSpec { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } feature("addSoon") { scenario("succeed case") { addSoon(1, 1) map { sum => assert(sum == 2) } } scenario("fail case") { addSoon(1, 1) map { sum => assert(sum == 3) } } } }
Running this version of
ExampleSpec
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: Feature: addSoon - Scenario: succeed case - Scenario: fail case *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncfeaturespec.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSpec extends AsyncFeatureSpec { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is designed to ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is designed to ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } feature("Simplicity") { // This test needs the actor fixture scenario("User needs to read test code written by others") { withActor { actor => actor ! Append("encourage clear code!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is designed to encourage clear code!") } } } // This test needs the database fixture scenario("User needs to understand what the tests are doing") { withDatabase { futureDb => futureDb map { db => db.append("be easy to reason about!") assert(db.toString === "ScalaTest is designed to be easy to reason about!") } } } // This test needs both the actor and the database scenario("User needs to write tests") { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("be easy to remember how to write!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("be easy to learn!") assert(db.toString === "ScalaTest is designed to be easy to learn!") assert(s === "ScalaTest is designed to be easy to remember how to write!") } } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncfeaturespec.oneargasynctest import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends fixture.AsyncFeatureSpec { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is designed to ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } feature("Simplicity") { scenario("User needs to read test code written by others") { actor => actor ! Append("encourage clear code!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is designed to encourage clear code!") } } scenario("User needs to understand what the tests are doing") { actor => actor ! Append("be easy to reason about!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is designed to be easy to reason about!") } } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFeatureSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncfeaturespec.beforeandafter import org.scalatest.AsyncFeatureSpec import org.scalatest.BeforeAndAfter import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends AsyncFeatureSpec with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is designed to ") // set up the fixture } after { actor ! Clear // clean up the fixture } feature("Simplicity") { scenario("User needs to read test code written by others") { actor ! Append("encourage clear code!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is designed to encourage clear code!") } } scenario("User needs to understand what the tests are doing") { actor ! Append("be easy to reason about!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is designed to be easy to reason about!") } } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncfeaturespec.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is designed to ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSpec extends AsyncFeatureSpec with Builder with Buffer { feature("Simplicity") { scenario("User needs to read test code written by others") { builderActor ! Append("encourage clear code!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is designed to encourage clear code!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } scenario("User needs to understand what the tests are doing") { builderActor ! Append("be easy to reason about!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is designed to be easy to reason about!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends AsyncFeatureSpec with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Spec extends AsyncFeatureSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncfeaturespec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is designed to ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSpec extends AsyncFeatureSpec with Builder with Buffer { feature("Simplicity") { scenario("User needs to read test code written by others") { builderActor ! Append("encourage clear code!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is designed to encourage clear code!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } scenario("User needs to understand what the tests are doing") { builderActor ! Append("be easy to reason about!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is designed to be easy to reason about!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFeatureSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFeatureSpec
that uses them, so that the tests they contain will be registered as tests in thatAsyncFeatureSpec
. For example, given thisStackActor
class:package org.scalatest.examples.asyncfeaturespec.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size >= 0, "size was less than zero") require(max >= size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => Future { if (buf.size != 0) StackInfo(Some(buf.remove(0)), buf.size, Max) else throw new IllegalStateException("can't pop an empty stack") } case Peek => Future { if (buf.size != 0) StackInfo(Some(buf(0)), buf.size, Max) else throw new IllegalStateException("can't peek an empty stack") } case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFeatureSpec
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncFeatureSpec
that uses them. If they are shared between differentAsyncFeatureSpec
s, however, you could also define them in a separate trait that is mixed into eachAsyncFeatureSpec
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncFeatureSpec trait AsyncFeatureSpecStackBehaviors { this: AsyncFeatureSpec => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { scenario("Size is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } scenario("Peek is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top == Some(lastItemAdded)) assert(afterPeek.size == beforePeek.size) } } scenario("Pop is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top == Some(lastItemAdded)) assert(afterPop.size == beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { scenario("Size is fired at non-full stack actor: " + name) { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } scenario("Push is fired at non-full stack actor: " + name) { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.size == beforePush.size + 1) assert(afterPush.top == Some(7)) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncFeatureSpec
offers a DSL for the purpose, which looks like this:scenariosFor(nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)) scenariosFor(nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName))
Here's an example:
class StackSpec extends AsyncFeatureSpec with AsyncFeatureSpecStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } feature("A Stack is pushed and popped") { scenario("Size is fired at empty stack actor") { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } scenario("Peek is fired at empty stack actor") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } scenario("Pop is fired at empty stack actor") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } scenariosFor(nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)) scenariosFor(nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName)) scenariosFor(nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)) scenariosFor(nonFullStackActor(almostFullStackActor, almostFullStackActorName)) scenario("full is invoked on a full stack") { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } scenariosFor(nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName)) scenario("push is invoked on a full stack") { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) StackSpec: Feature: A Stack actor - Scenario: Size is fired at empty stack actor - Scenario: Peek is fired at empty stack actor - Scenario: Pop is fired at empty stack actor - Scenario: Size is fired at non-empty stack actor: almost empty stack actor - Scenario: Peek is fired at non-empty stack actor: almost empty stack actor - Scenario: Pop is fired at non-empty stack actor: almost empty stack actor - Scenario: Size is fired at non-full stack actor: almost empty stack actor - Scenario: Push is fired at non-full stack actor: almost empty stack actor - Scenario: Size is fired at non-empty stack actor: almost full stack actor - Scenario: Peek is fired at non-empty stack actor: almost full stack actor - Scenario: Pop is fired at non-empty stack actor: almost full stack actor - Scenario: Size is fired at non-full stack actor: almost full stack actor - Scenario: Push is fired at non-full stack actor: almost full stack actor - Scenario: Size is fired at full stack actor - Scenario: Size is fired at non-empty stack actor: full stack actor - Scenario: Peek is fired at non-empty stack actor: full stack actor - Scenario: Pop is fired at non-empty stack actor: full stack actor - Scenario: Push is fired at full stack actor
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Although in an
AsyncFeatureSpec
, thefeature
clause is a nesting construct analogous toAsyncFunSpec
'sdescribe
clause, you many sometimes need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in anAsyncFeatureSpec
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFeatureSpecStackBehaviors
example.Given this
AsyncFeatureSpecStackBehaviors
trait, calling it with thealmostEmptyStackActor
fixture, like this:scenariosFor(nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName))
yields test names:
Size is fired at non-empty stack actor: almost empty stack actor
Peek is fired at non-empty stack actor: almost empty stack actor
Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
almostFullStackActor
fixture, like this:scenariosFor(nonEmptyStack(almostFullStackActor, lastValuePushed, almostFullStackActorName))
yields different test names:
Size is fired at non-empty stack actor: almost full stack actor
Peek is fired at non-empty stack actor: almost full stack actor
Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncFeatureSpecLike
extends AsyncTestSuite with AsyncTestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncFeatureSpec
, which represents a suite of tests in which each test represents one scenario of a feature.Implementation trait for class
AsyncFeatureSpec
, which represents a suite of tests in which each test represents one scenario of a feature.AsyncFeatureSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncFeatureSpec
into some other class, you can use this trait instead, because classAsyncFeatureSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncFeatureSpec
. -
abstract
class
AsyncFlatSpec
extends AsyncFlatSpecLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
FlatSpec
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
FlatSpec
tests.Recommended Usage: AsyncFlatSpec
is intended to enable users ofFlatSpec
to write non-blocking asynchronous tests that are consistent with their traditionalFlatSpec
tests. Note:AsyncFlatSpec
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFlatSpec
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Trait
AsyncFlatSpec
is so named because your specification text and tests line up flat against the left-side indentation level, with no nesting needed. Here's an exampleAsyncFlatSpec
:package org.scalatest.examples.asyncflatspec import org.scalatest.AsyncFlatSpec import scala.concurrent.Future class AddSpec extends AsyncFlatSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } behavior of "addSoon" it should "eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum "addNow" should "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
The initial test in this example demonstrates the use of an explicit
behavior of
clause, which establishesaddSoon
as the subject. The second test demonstrates the alternate syntax of replacing the firstit
with the subject string, in this case,"addNow"
. As with traditionalFlatSpec
s, you can usemust
orcan
as well asshould
. For example, instead ofit should "eventually
..., you could writeit must "eventually
... orit can "eventually
.... You can also writethey
instead ofit
. See the documentation forFlatSpec
for more detail.Running the above
AddSpec
in the Scala interpreter would yield:addSoon - should eventually compute a sum of passed Ints - should immediately compute a sum of passed Ints
Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. For clarity, here's the relevant code in a REPL session:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext scala> implicit val executionContext = ExecutionContext.Implicits.global executionContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@26141c5b scala> def addSoon(addends: Int*): Future[Int] = Future { addends.sum } addSoon: (addends: Int*)scala.concurrent.Future[Int] scala> val futureSum: Future[Int] = addSoon(1, 2) futureSum: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@721f47b2 scala> futureSum map { sum => assert(sum == 3) } res0: scala.concurrent.Future[org.scalatest.Assertion] = scala.concurrent.impl.Promise$DefaultPromise@3955cfcb
The second test has result type
Assertion
:scala> def addNow(addends: Int*): Int = addends.sum addNow: (addends: Int*)Int scala> val sum: Int = addNow(1, 2) sum: Int = 3 scala> assert(sum == 3) res1: org.scalatest.Assertion = Succeeded
When
AddSpec
is constructed, the second test will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker:"addNow" should "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) assert(sum == 3) println("hi") // println has result type Unit succeed // succeed has result type Assertion }
An
AsyncFlatSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
it
method while theAsyncFlatSpec
is in its registration phase. Any attempt to register a test after theAsyncFlatSpec
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncFlatSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncFlatSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Asynchronous execution model
AsyncFlatSpec
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncFlatSpec
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
FlatSpec
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncFlatSpec
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncFlatSpec
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asFlatSpec
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncFlatSpec
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncFlatSpec
provides two ways to ignore a test, both demonstrated in the following example:package org.scalatest.examples.asyncflatspec.ignore import org.scalatest.AsyncFlatSpec import scala.concurrent.Future class AddSpec extends AsyncFlatSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } behavior of "addSoon" ignore should "eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum "addNow" should "immediately compute a sum of passed Ints" ignore { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
In the first test,
ignore
is used instead ofit
. In the second test, which uses the shorthand notation, noit
exists to change intoignore
. To ignore such tests, you must instead changein
toignore
, as shown in the above example. If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will report both tests as ignored:
AddSpec: addSoon - should eventually compute a sum of passed Ints !!! IGNORED !!! addNow - should immediately compute a sum of passed Ints !!! IGNORED !!!
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncflatspec.ignoreall import org.scalatest.AsyncFlatSpec import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSpec extends AsyncFlatSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should "eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum "addNow" should "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSpec
in the Scala interpreter, you'll see:AddSpec: addSoon - should eventually compute a sum of passed Ints !!! IGNORED !!! addNow - should immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
it
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncFlatSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byAsyncFlatSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byAsyncFlatSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.asyncflatspec.info import collection.mutable import org.scalatest._ class SetSpec extends AsyncFlatSpec with GivenWhenThen { "A mutable Set" should "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") succeed } }
If you run this
AsyncFlatSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
AsyncFlatSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
AsyncFlatSpec
that usesmarkup
:package org.scalatest.examples.asyncflatspec.markup import collection.mutable import org.scalatest._ class SetSpec extends AsyncFlatSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" should "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncflatspec.note import collection.mutable import org.scalatest._ class SetSpec extends AsyncFlatSpec { "A mutable Set" should "allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncflatspec.pending import org.scalatest.AsyncFlatSpec import scala.concurrent.Future class AddSpec extends AsyncFlatSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should "eventually compute a sum of passed Ints" in (pending) def addNow(addends: Int*): Int = addends.sum "addNow" should "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run both tests, but report that first test is pending. You'll see:
AddSpec: addSoon - should eventually compute a sum of passed Ints (pending) addNow - should immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFlatSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFlatSpec
, groups of tests can optionally be included and/or excluded. To tag anAsyncFlatSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFlatSpec
s like this:package org.scalatest.examples.asyncflatspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFlatSpec
tests into groups with tags like this:import org.scalatest.AsyncFlatSpec import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSpec extends AsyncFlatSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should "eventually compute a sum of passed Ints" taggedAs(Slow) in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum "addNow" should "immediately compute a sum of passed Ints" taggedAs(Slow, DbTest) in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFlatSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncflatspec.getfixture import org.scalatest.AsyncFlatSpec import scala.concurrent.Future class ExampleSpec extends AsyncFlatSpec { def fixture: Future[String] = Future { "ScalaTest is " } "Testing" should "be easy" in { val future = fixture val result = future map { s => s + "easy!" } result map { s => assert(s == "ScalaTest is easy!") } } it should "be fun" in { val future = fixture val result = future map { s => s + "fun!" } result map { s => assert(s == "ScalaTest is fun!") } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFlatSpec
.Trait
AsyncFlatSpec
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncflatspec.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSpec extends AsyncFlatSpec { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "This test" should "succeed" in { addSoon(1, 1) map { sum => assert(sum == 2) } } it should "fail" in { addSoon(1, 1) map { sum => assert(sum == 3) } } }
Running this version of
ExampleSpec
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncflatspec.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSpec extends AsyncFlatSpec { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } // This test needs the actor fixture "Testing" should "be productive" in { withActor { actor => actor ! Append("productive!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is productive!") } } } // This test needs the database fixture "Test code" should "be readable" in { withDatabase { futureDb => futureDb map { db => db.append("readable!") assert(db.toString == "ScalaTest is readable!") } } } // This test needs both the actor and the database it should "be clear and concise" in { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("concise!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("clear!") assert(db.toString == "ScalaTest is clear!") assert(s == "ScalaTest is concise!") } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncflatspec.oneargasynctest import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends fixture.AsyncFlatSpec { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } "Testing" should "be easy" in { actor => actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } it should "be fun" in { actor => actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFlatSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncflatspec.beforeandafter import org.scalatest.AsyncFlatSpec import org.scalatest.BeforeAndAfter import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends AsyncFlatSpec with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is ") // set up the fixture } after { actor ! Clear // clean up the fixture } "Testing" should "be easy" in { actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } it should "be fun" in { actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncflatspec.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSpec extends AsyncFlatSpec with Builder with Buffer { "Testing" should "be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } it should "be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends AsyncFlatSpec with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Spec extends AsyncFlatSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncflatspec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSpec extends AsyncFlatSpec with Builder with Buffer { "Testing" should "be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } it should "be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFlatSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFlatSpec
that uses them, so that the tests they contain will be registered as tests in thatAsyncFlatSpec
. For example, given thisStackActor
class:package org.scalatest.examples.asyncflatspec.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size > 0, "size was less than zero") require(max > size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => Future { if (buf.size != 0) StackInfo(Some(buf.remove(0)), buf.size, Max) else throw new IllegalStateException("can't pop an empty stack") } case Peek => Future { if (buf.size != 0) StackInfo(Some(buf(0)), buf.size, Max) else throw new IllegalStateException("can't peek an empty stack") } case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFlatSpec
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncFlatSpec
that uses them. If they are shared between differentAsyncFlatSpec
s, however, you could also define them in a separate trait that is mixed into eachAsyncFlatSpec
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncFlatSpec trait AsyncFlatSpecStackBehaviors { this: AsyncFlatSpec => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { it should ("return non-empty StackInfo when Size is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } it should ("return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top == Some(lastItemAdded)) assert(afterPeek.size == beforePeek.size) } } it should ("return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top == Some(lastItemAdded)) assert(afterPop.size == beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { it should ("return non-full StackInfo when Size is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } it should ("return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.top == Some(7)) assert(afterPush.size == beforePush.size + 1) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncFlatSpec
offers a DSL for the purpose, which looks like this:it should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) it should behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName)
Here's an example:
class StackSpec extends AsyncFlatSpec with AsyncFlatSpecStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } "A Stack actor (when empty)" should "return empty StackInfo when Size is fired at it" in { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } it should "complain when Peek is fired at it" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } it should "complain when Pop is fired at it" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } "A Stack actor (when non-empty)" should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) it should behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName) it should behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName) it should behave like nonFullStackActor(almostFullStackActor, almostFullStackActorName) "A Stack actor (when full)" should "return full StackInfo when Size is fired at it" in { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } it should behave like nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName) it should "complain when Push is fired at it" in { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) StackSpec: A Stack actor (when empty) - should return empty StackInfo when Size is fired at it - should complain when Peek is fired at it - should complain when Pop is fired at it A Stack actor (when non-empty) - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost empty stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost empty stack actor - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost full stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost full stack actor A Stack actor (when full) - should return full StackInfo when Size is fired at it - should return non-empty StackInfo when Size is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: full stack actor - should complain when Push is fired at it
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Although in an
AsyncFlatSpec
, thebehavior of
clause is a nesting construct analogous toAsyncFunSpec
'sdescribe
clause, you many sometimes need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in anAsyncFlatSpec
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFlatSpecStackBehaviors
example.Given this
AsyncFlatSpecStackBehaviors
trait, calling it with thealmostEmptyStackActor
fixture, like this:"A Stack actor (when non-empty)" should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)
yields test names:
A Stack actor (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor
A Stack actor (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor
A Stack actor (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
almostFullStackActor
fixture, like this:it should behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)
yields different test names:
A Stack actor (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor
A Stack actor (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor
A Stack actor (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncFlatSpecLike
extends AsyncTestSuite with AsyncTestRegistration with ShouldVerb with MustVerb with CanVerb with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncFlatSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
AsyncFlatSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.AsyncFlatSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncFlatSpec
into some other class, you can use this trait instead, because classAsyncFlatSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncFlatSpec
. -
abstract
class
AsyncFreeSpec
extends AsyncFreeSpecLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
FreeSpec
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
FreeSpec
tests.Recommended Usage: AsyncFreeSpec
is intended to enable users ofFreeSpec
to write non-blocking asynchronous tests that are consistent with their traditionalFreeSpec
tests. Note:AsyncFreeSpec
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFreeSpec
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Here's an example
AsyncFreeSpec
:package org.scalatest.examples.asyncfreespec import org.scalatest.AsyncFreeSpec import scala.concurrent.Future class AddSpec extends AsyncFreeSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" - { "will eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" - { "will immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
In an
AsyncFreeSpec
you write a test with a string followed byin
and the body of the test in curly braces, like this:"will eventually compute a sum of passed Ints" in { // ... }
You can nest a test inside any number of description clauses, which you write with a string followed by a dash character and a block, like this:
"addSoon" - { // ... }
You can nest description clauses as deeply as you want. Because the description clause is denoted with an operator, not a word like
should
, you are free to structure the text however you wish. In short, you structure anAsyncFreeSpec
exactly like aFreeSpec
, but with tests having result typeAssertion
orFuture[Assertion]
. For more examples of structure, see the documentation forFreeSpec
.Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. For clarity, here's the relevant code in a REPL session:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext scala> implicit val executionContext = ExecutionContext.Implicits.global executionContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@26141c5b scala> def addSoon(addends: Int*): Future[Int] = Future { addends.sum } addSoon: (addends: Int*)scala.concurrent.Future[Int] scala> val futureSum: Future[Int] = addSoon(1, 2) futureSum: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@721f47b2 scala> futureSum map { sum => assert(sum == 3) } res0: scala.concurrent.Future[org.scalatest.Assertion] = scala.concurrent.impl.Promise$DefaultPromise@3955cfcb
The second test has result type
Assertion
:scala> def addNow(addends: Int*): Int = addends.sum addNow: (addends: Int*)Int scala> val sum: Int = addNow(1, 2) sum: Int = 3 scala> assert(sum == 3) res1: org.scalatest.Assertion = Succeeded
When
AddSpec
is constructed, the second test will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker:"will immediately compute a sum of passed Ints" - { val sum: Int = addNow(1, 2) assert(sum == 3) println("hi") // println has result type Unit succeed // succeed has result type Assertion }
An
AsyncFreeSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
it
method while theAsyncFreeSpec
is in its registration phase. Any attempt to register a test after theAsyncFreeSpec
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncFreeSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncFreeSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Asynchronous execution model
AsyncFreeSpec
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncFreeSpec
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
FreeSpec
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncFreeSpec
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncFreeSpec
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asFreeSpec
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncFreeSpec
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncFreeSpec
adds a methodignore
to strings that can be used instead ofin
to register a test. For example, to temporarily disable the test with the name"addSoon will eventually compute a sum of passed Ints"
, just change “in
” into “ignore
,” like this:package org.scalatest.examples.asyncfreespec.ignore import org.scalatest.AsyncFreeSpec import scala.concurrent.Future class AddSpec extends AsyncFreeSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" - { "will eventually compute a sum of passed Ints" ignore { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" - { "will immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
If you run this version of
AddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run only the second test and report that the first test was ignored:
AddSpec: addSoon - will eventually compute a sum of passed Ints !!! IGNORED !!! addNow - will immediately compute a sum of passed Ints
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncfreespec.ignoreall import org.scalatest.AsyncFreeSpec import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSpec extends AsyncFreeSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" - { "will eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" - { "will immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSpec
in the Scala interpreter, you'll see:AddSpec: addSoon - will eventually compute a sum of passed Ints !!! IGNORED !!! addNow - will immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
it
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncFreeSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byAsyncFreeSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byAsyncFreeSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.asyncfreespec.info import collection.mutable import org.scalatest._ class SetSpec extends AsyncFreeSpec with GivenWhenThen { "A mutable Set" - { "should allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") succeed } } }
If you run this
AsyncFreeSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
AsyncFreeSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
AsyncFreeSpec
that usesmarkup
:package org.scalatest.examples.asyncfreespec.markup import collection.mutable import org.scalatest._ class SetSpec extends AsyncFreeSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" - { "should allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncfreespec.note import collection.mutable import org.scalatest._ class SetSpec extends AsyncFreeSpec { "A mutable Set" - { "should allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncfreespec.pending import org.scalatest.AsyncFreeSpec import scala.concurrent.Future class AddSpec extends AsyncFreeSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" - { "will eventually compute a sum of passed Ints" in (pending) } def addNow(addends: Int*): Int = addends.sum "addNow" - { "will immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run both tests, but report that first test is pending. You'll see:
AddSpec: addSoon - will eventually compute a sum of passed Ints (pending) addNow - will immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFreeSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFreeSpec
, groups of tests can optionally be included and/or excluded. To tag anAsyncFreeSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFreeSpec
s like this:package org.scalatest.examples.asyncfreespec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFreeSpec
tests into groups with tags like this:import org.scalatest.AsyncFreeSpec import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSpec extends AsyncFreeSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" - { "will eventually compute a sum of passed Ints" taggedAs(Slow) in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" - { "will immediately compute a sum of passed Ints" taggedAs(Slow, DbTest) in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFreeSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncfreespec.getfixture import org.scalatest.AsyncFreeSpec import scala.concurrent.Future class ExampleSpec extends AsyncFreeSpec { def fixture: Future[String] = Future { "ScalaTest is " } "Testing" - { "should be easy" in { val future = fixture val result = future map { s => s + "easy!" } result map { s => assert(s == "ScalaTest is easy!") } } "should be fun" in { val future = fixture val result = future map { s => s + "fun!" } result map { s => assert(s == "ScalaTest is fun!") } } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFreeSpec
.Trait
AsyncFreeSpec
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncfreespec.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSpec extends AsyncFreeSpec { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "This test" - { "should succeed" in { addSoon(1, 1) map { sum => assert(sum == 2) } } "should fail" in { addSoon(1, 1) map { sum => assert(sum == 3) } } } }
Running this version of
ExampleSpec
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncfreespec.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSpec extends AsyncFreeSpec { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } "Testing" - { // This test needs the actor fixture "should be productive" in { withActor { actor => actor ! Append("productive!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is productive!") } } } } "Test code" - { // This test needs the database fixture "should be readable" in { withDatabase { futureDb => futureDb map { db => db.append("readable!") assert(db.toString == "ScalaTest is readable!") } } } // This test needs both the actor and the database "should be clear and concise" in { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("concise!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("clear!") assert(db.toString == "ScalaTest is clear!") assert(s == "ScalaTest is concise!") } } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncfreespec.oneargasynctest import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends fixture.AsyncFreeSpec { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } "Testing" - { "should be easy" in { actor => actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } "should be fun" in { actor => actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFreeSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncfreespec.beforeandafter import org.scalatest.AsyncFreeSpec import org.scalatest.BeforeAndAfter import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends AsyncFreeSpec with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is ") // set up the fixture } after { actor ! Clear // clean up the fixture } "Testing" - { "should be easy" in { actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } "should be fun" in { actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncfreespec.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSpec extends AsyncFreeSpec with Builder with Buffer { "Testing" - { "should be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } "should be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends AsyncFreeSpec with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Spec extends AsyncFreeSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncfreespec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSpec extends AsyncFreeSpec with Builder with Buffer { "Testing" - { "should be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } "should be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFreeSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFreeSpec
that uses them, so that the tests they contain will be registered as tests in thatAsyncFreeSpec
. For example, given thisStackActor
class:package org.scalatest.examples.asyncfreespec.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size > 0, "size was less than zero") require(max >= size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => Future { if (buf.size != 0) StackInfo(Some(buf.remove(0)), buf.size, Max) else throw new IllegalStateException("can't pop an empty stack") } case Peek => Future { if (buf.size != 0) StackInfo(Some(buf(0)), buf.size, Max) else throw new IllegalStateException("can't peek an empty stack") } case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFreeSpec
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncFreeSpec
that uses them. If they are shared between differentAsyncFreeSpec
s, however, you could also define them in a separate trait that is mixed into eachAsyncFreeSpec
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncFreeSpec trait AsyncFreeSpecStackBehaviors { this: AsyncFreeSpec => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { ("return non-empty StackInfo when Size is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } ("return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top == Some(lastItemAdded)) assert(afterPeek.size == beforePeek.size) } } ("return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top == Some(lastItemAdded)) assert(afterPop.size == beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { ("return non-full StackInfo when Size is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } ("return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.top == Some(7)) assert(afterPush.size == beforePush.size + 1) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncFreeSpec
offers a DSL for the purpose, which looks like this:behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)
Here's an example:
class StackSpec extends AsyncFreeSpec with AsyncFreeSpecStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } "A Stack" - { "(when empty)" - { "should be empty" in { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } "should complain on peek" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } "should complain on pop" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } } "(with one item)" - { "should" - { behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName) } } "(with one item less than capacity)" - { "should" - { behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName) behave like nonFullStackActor(almostFullStackActor, almostFullStackActorName) } } "(full)" - { "should be full" in { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } "should" - { behave like nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName) } "should complain on a push" in { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) StackSpec: A Stack (when empty) - should be empty - should complain on peek - should complain on pop (with one item) should - return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor - return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor - return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor - return non-full StackInfo when Size is fired at non-full stack actor: almost empty stack actor - return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost empty stack actor (with one item less than capacity) should - return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor - return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor - return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor - return non-full StackInfo when Size is fired at non-full stack actor: almost full stack actor - return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost full stack actor (full) - should be full should - return non-empty StackInfo when Size is fired at non-empty stack actor: full stack actor - return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: full stack actor - return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: full stack actor - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Although in an
AsyncFreeSpec
, the-
clause is a nesting construct analogous toAsyncFunSpec
'sdescribe
clause, you many sometimes need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in anAsyncFreeSpec
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFreeSpecStackBehaviors
example.Given this
AsyncFreeSpecStackBehaviors
trait, calling it with thealmostEmptyStackActor
fixture, like this:behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)
yields test names:
A Stack (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
almostFullStackActor
fixture, like this:behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)
yields different test names:
A Stack (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncFreeSpecLike
extends AsyncTestSuite with AsyncTestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncFreeSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (-
).Implementation trait for class
AsyncFreeSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (-
).AsyncFreeSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncFreeSpec
into some other class, you can use this trait instead, because classAsyncFreeSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncFreeSpec
. -
abstract
class
AsyncFunSpec
extends AsyncFunSpecLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
FunSpec
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
FunSpec
tests.Recommended Usage: AsyncFunSpec
is intended to enable users ofFunSpec
to write non-blocking asynchronous tests that are consistent with their traditionalFunSpec
tests. Note:AsyncFunSpec
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFunSpec
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Here's an example
AsyncFunSpec
:package org.scalatest.examples.asyncfunspec import org.scalatest.AsyncFunSpec import scala.concurrent.Future class AddSpec extends AsyncFunSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("addSoon") { it("will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum describe("addNow") { it("will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
An
AsyncFunSpec
contains describe clauses and tests. You define a describe clause withdescribe
, and a test with eitherit
orthey
.describe
,it
, andthey
are methods, defined inAsyncFunSpec
, which will be invoked by the primary constructor ofAddSpec
. A describe clause names, or gives more information about, the subject (class or other entity) you are specifying and testing. In the previous example,"addSoon"
and"addNow"
are the subjects under specification and test. With each test you provide a string (the spec text) that specifies one bit of behavior of the subject, and a block of code that tests that behavior. You place the spec text between the parentheses, followed by the test code between curly braces. The test code will be wrapped up as a function passed as a by-name parameter toit
(orthey
), which will register the test for later execution.Note: the
they
method is intended for use when the subject is plural, for example:describe("The combinators") { they("should be easy to learn") { succeed } they("should be efficient") { succeed } they("should do something cool") { succeed } }
Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. For clarity, here's the relevant code in a REPL session:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext scala> implicit val executionContext = ExecutionContext.Implicits.global executionContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@26141c5b scala> def addSoon(addends: Int*): Future[Int] = Future { addends.sum } addSoon: (addends: Int*)scala.concurrent.Future[Int] scala> val futureSum: Future[Int] = addSoon(1, 2) futureSum: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@721f47b2 scala> futureSum map { sum => assert(sum == 3) } res0: scala.concurrent.Future[org.scalatest.Assertion] = scala.concurrent.impl.Promise$DefaultPromise@3955cfcb
The second test has result type
Assertion
:scala> def addNow(addends: Int*): Int = addends.sum addNow: (addends: Int*)Int scala> val sum: Int = addNow(1, 2) sum: Int = 3 scala> assert(sum == 3) res1: org.scalatest.Assertion = Succeeded
When
AddSpec
is constructed, the second test will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker:it("will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) assert(sum == 3) println("hi") // println has result type Unit succeed // succeed has result type Assertion }
An
AsyncFunSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
it
method while theAsyncFunSpec
is in its registration phase. Any attempt to register a test after theAsyncFunSpec
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncFunSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncFunSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Asynchronous execution model
AsyncFunSpec
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncFunSpec
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
FunSpec
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncFunSpec
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncFunSpec
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asFunSpec
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncFunSpec
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncFunSpec
provides registration methods that start withignore
instead ofit
orthey
. For example, to temporarily disable the test with the text"will eventually compute a sum of passed Ints"
, just change “it
” into “ignore
,” like this:package org.scalatest.examples.asyncfunspec.ignore import org.scalatest.AsyncFunSpec import scala.concurrent.Future class AddSpec extends AsyncFunSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("addSoon") { ignore("will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum describe("addNow") { it("will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
If you run this version of
AddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run only the second test and report that the first test was ignored:
AddSpec: addSoon - will eventually compute a sum of passed Ints !!! IGNORED !!! addNow - will immediately compute a sum of passed Ints
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncfunspec.ignoreall import org.scalatest.AsyncFunSpec import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSpec extends AsyncFunSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("addSoon") { it("will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum describe("addNow") { it("will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSpec
in the Scala interpreter, you'll see:AddSpec: addSoon - will eventually compute a sum of passed Ints !!! IGNORED !!! addNow - will immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
it
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncFunSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byAsyncFunSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via one of itsapply
methods. TheInformer
will then pass the information to theReporter
via anInfoProvided
event. Here's an example in which theInformer
returned byinfo
is used implicitly by theGiven
,When
, andThen
methods of traitGivenWhenThen
:package org.scalatest.examples.asyncfunspec.info import collection.mutable import org.scalatest._ class SetSpec extends AsyncFunSpec with GivenWhenThen { describe("A mutable Set") { it("should allow an element to be added") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") succeed } } }
If you run this
AsyncFunSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
AsyncFunSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
AsyncFunSpec
that usesmarkup
:package org.scalatest.examples.asyncfunspec.markup import collection.mutable import org.scalatest._ class SetSpec extends AsyncFunSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } describe("A mutable Set") { it("should allow an element to be added") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncfunspec.note import collection.mutable import org.scalatest._ class SetSpec extends AsyncFunSpec { describe("A mutable Set") { it("should allow an element to be added") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncfunspec.pending import org.scalatest.AsyncFunSpec import scala.concurrent.Future class AddSpec extends AsyncFunSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("addSoon") { it("will eventually compute a sum of passed Ints")(pending) } def addNow(addends: Int*): Int = addends.sum describe("addNow") { it("will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run both tests, but report that first test is pending. You'll see:
AddSpec: addSoon - will eventually compute a sum of passed Ints (pending) addNow - will immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFunSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFunSpec
, groups of tests can optionally be included and/or excluded. To tag anAsyncFunSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFunSpec
s like this:package org.scalatest.examples.asyncfunspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFunSpec
tests into groups with tags like this:import org.scalatest.AsyncFunSpec import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSpec extends AsyncFunSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("addSoon") { it("will eventually compute a sum of passed Ints", Slow) { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum describe("addNow") { it("will immediately compute a sum of passed Ints", Slow, DbTest) { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFunSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncfunspec.getfixture import org.scalatest.AsyncFunSpec import scala.concurrent.Future class ExampleSpec extends AsyncFunSpec { def fixture: Future[String] = Future { "ScalaTest is " } describe("Testing") { it("should be easy") { val future = fixture val result = future map { s => s + "easy!" } result map { s => assert(s == "ScalaTest is easy!") } } it("should be fun") { val future = fixture val result = future map { s => s + "fun!" } result map { s => assert(s == "ScalaTest is fun!") } } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFunSpec
.Trait
AsyncFunSpec
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncfunspec.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSpec extends AsyncFunSpec { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } describe("This test") { it("should succeed") { addSoon(1, 1) map { sum => assert(sum == 2) } } it("should fail") { addSoon(1, 1) map { sum => assert(sum == 3) } } } }
Running this version of
ExampleSpec
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncfunspec.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSpec extends AsyncFunSpec { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } describe("Testing") { // This test needs the actor fixture it("should be productive") { withActor { actor => actor ! Append("productive!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is productive!") } } } } describe("Test code") { // This test needs the database fixture it("should be readable") { withDatabase { futureDb => futureDb map { db => db.append("readable!") assert(db.toString == "ScalaTest is readable!") } } } // This test needs both the actor and the database it("should be clear and concise") { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("concise!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("clear!") assert(db.toString == "ScalaTest is clear!") assert(s == "ScalaTest is concise!") } } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncfunspec.oneargasynctest import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends fixture.AsyncFunSpec { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } describe("Testing") { it("should be easy") { actor => actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } it("should be fun") { actor => actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFunSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncfunspec.beforeandafter import org.scalatest.AsyncFunSpec import org.scalatest.BeforeAndAfter import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends AsyncFunSpec with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is ") // set up the fixture } after { actor ! Clear // clean up the fixture } describe("Testing") { it("should be easy") { actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } it("should be fun") { actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncfunspec.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSpec extends AsyncFunSpec with Builder with Buffer { describe("Testing") { it("should be easy") { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } it("should be fun") { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends AsyncFunSpec with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Spec extends AsyncFunSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncfunspec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSpec extends AsyncFunSpec with Builder with Buffer { describe("Testing") { it("should be easy") { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } it("should be fun") { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFunSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFunSpec
that uses them, so that the tests they contain will be registered as tests in thatAsyncFunSpec
. For example, given thisStackActor
class:package org.scalatest.examples.asyncfunspec.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size > 0, "size was less than zero") require(max > size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => Future { if (buf.size != 0) StackInfo(Some(buf.remove(0)), buf.size, Max) else throw new IllegalStateException("can't pop an empty stack") } case Peek => Future { if (buf.size != 0) StackInfo(Some(buf(0)), buf.size, Max) else throw new IllegalStateException("can't peek an empty stack") } case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFunSpec
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncFunSpec
that uses them. If they are shared between differentAsyncFunSpec
s, however, you could also define them in a separate trait that is mixed into eachAsyncFunSpec
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncFunSpec trait AsyncFunSpecStackBehaviors { this: AsyncFunSpec => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { it("should return non-empty StackInfo when Size is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } it("should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top == Some(lastItemAdded)) assert(afterPeek.size == beforePeek.size) } } it("should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top == Some(lastItemAdded)) assert(afterPop.size == beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { it("should return non-full StackInfo when Size is fired at non-full stack actor: " + name) { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } it("should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: " + name) { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.top == Some(7)) assert(afterPush.size == beforePush.size + 1) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncFunSpec
offers a DSL for the purpose, which looks like this:it should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) it should behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName)
Here's an example:
class StackSpec extends AsyncFunSpec with AsyncFunSpecStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } describe("A Stack") { describe("(when empty)") { it("should be empty") { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } it("should complain on peek") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } it("should complain on pop") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } } describe("(when non-empty)") { it should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) it should behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName) it should behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName) it should behave like nonFullStackActor(almostFullStackActor, almostFullStackActorName) } describe("(when full)") { it("should be full") { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } it should behave like nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName) it("should complain on a push") { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) StackSpec: A Stack (when empty) - should be empty - should complain on peek - should complain on pop (when non-empty) - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost empty stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost empty stack actor - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost full stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost full stack actor (when full) - should be full - should return non-empty StackInfo when Size is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: full stack actor - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Therefore, you need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in an
AsyncFunSpec
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFunSpecStackBehaviors
example.Given this
AsyncFunSpecStackBehaviors
trait, calling it with thestackWithOneItem
fixture, like this:it should behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)
yields test names:
A Stack (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
stackWithOneItemLessThanCapacity
fixture, like this:it should behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)
yields different test names:
A Stack (when non-empty) should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor
A Stack (when non-empty) should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncFunSpecLike
extends AsyncTestSuite with AsyncTestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncFunSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
AsyncFunSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.AsyncFunSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncFunSpec
into some other class, you can use this trait instead, because classAsyncFunSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncFunSpec
. -
abstract
class
AsyncFunSuite
extends AsyncFunSuiteLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
FunSuite
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
FunSuite
tests.Recommended Usage: AsyncFunSuite
is intended to enable users ofFunSuite
to write non-blocking asynchronous tests that are consistent with their traditionalFunSuite
tests. Note:AsyncFunSuite
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFunSuite
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Here's an example
AsyncFunSuite
:package org.scalatest.examples.asyncfunsuite import org.scalatest.AsyncFunSuite import scala.concurrent.Future class AddSuite extends AsyncFunSuite { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } test("addSoon will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum test("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests, which // must result in type Assertion: assert(sum == 3) } }
“
test
” is a method, defined inAsyncFunSuite
, which will be invoked by the primary constructor ofAddSuite
. 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 totest
, which registers it for later execution. The result type of the by-name in anAsyncFunSuite
must beFuture[Assertion]
.Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. For clarity, here's the relevant code in a REPL session:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext scala> implicit val executionContext = ExecutionContext.Implicits.global executionContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@26141c5b scala> def addSoon(addends: Int*): Future[Int] = Future { addends.sum } addSoon: (addends: Int*)scala.concurrent.Future[Int] scala> val futureSum: Future[Int] = addSoon(1, 2) futureSum: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@721f47b2 scala> futureSum map { sum => assert(sum == 3) } res0: scala.concurrent.Future[org.scalatest.Assertion] = scala.concurrent.impl.Promise$DefaultPromise@3955cfcb
The second test has result type
Assertion
:scala> def addNow(addends: Int*): Int = addends.sum addNow: (addends: Int*)Int scala> val sum: Int = addNow(1, 2) sum: Int = 3 scala> assert(sum == 3) res1: org.scalatest.Assertion = Succeeded
When
AddSuite
is constructed, the second test will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker:test("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) assert(sum == 3) println("hi") // println has result type Unit succeed // succeed has result type Assertion }
An
AsyncFunSuite
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
test
method while theAsyncFunSuite
is in its registration phase. Any attempt to register a test after theAsyncFunSuite
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncFunSuite
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncFunSuite
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Asynchronous execution model
AsyncFunSuite
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncFunSuite
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
FunSuite
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncFunSuite
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncFunSuite
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asFunSuite
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncFunSuite
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncFunSuite
provides registration methods that start withignore
instead oftest
. Here's an example:package org.scalatest.examples.asyncfunsuite.ignore import org.scalatest.AsyncFunSuite import scala.concurrent.Future class AddSuite extends AsyncFunSuite { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } ignore("addSoon will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum test("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
If you run this version of
AddSuite
with:scala> org.scalatest.run(new AddSuite)
It will run only the second test and report that the first test was ignored:
AddSuite: - addSoon will eventually compute a sum of passed Ints !!! IGNORED !!! - addNow will immediately compute a sum of passed Ints
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncfunsuite.ignoreall import org.scalatest.AsyncFunSuite import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSuite extends AsyncFunSuite { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } test("addSoon will eventually compute a sum of passed Ints") { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum test("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSuite
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSuite
in the Scala interpreter, you'll see:scala> org.scalatest.run(new AddSuite) AddSuite: - addSoon will eventually compute a sum of passed Ints !!! IGNORED !!! - addNow will immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
test
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncFunSuite
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byAsyncFunSuite
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event. Here's an example that shows both a direct use as well as an indirect use through the methods ofGivenWhenThen
:package org.scalatest.examples.asyncfunsuite.info import collection.mutable import org.scalatest._ class SetSuite extends AsyncFunSuite with GivenWhenThen { test("An element can be added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") succeed } }
If you run this
AsyncFunSuite
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSuite) SetSuite: - an element can be added to an empty mutable Set + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
AsyncFunSuite
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
AsyncFunSuite
that usesmarkup
:package org.scalatest.examples.asyncfunsuite.markup import collection.mutable import org.scalatest._ class SetSuite extends AsyncFunSuite with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } test("An element can be added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncfunsuite.note import collection.mutable import org.scalatest._ class SetSuite extends AsyncFunSuite { test("An element can be added to an empty mutable Set") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSuite: + notes are sent immediately + alerts are also sent immediately - An element can be added to an empty mutable Set + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncfunsuite.pending import org.scalatest.AsyncFunSuite import scala.concurrent.Future class AddSuite extends AsyncFunSuite { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } test("addSoon will eventually compute a sum of passed Ints") (pending) def addNow(addends: Int*): Int = addends.sum test("addNow will immediately compute a sum of passed Ints") { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSuite
with:scala> org.scalatest.run(new AddSuite)
It will run both tests, but report that first test is pending. You'll see:
AddSuite: - addSoon will eventually compute a sum of passed Ints (pending) - addNow will immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFunSuite
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFunSuite
, groups of tests can optionally be included and/or excluded. To tag anAsyncFunSuite
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFunSuite
s like this:package org.scalatest.examples.asyncfunsuite.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFunSuite
tests into groups with tags like this:import org.scalatest.AsyncFunSuite import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSuite extends AsyncFunSuite { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } test("addSoon will eventually compute a sum of passed Ints", Slow) { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } def addNow(addends: Int*): Int = addends.sum test("addNow will immediately compute a sum of passed Ints", Slow, DbTest) { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFunSuite
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncfunsuite.getfixture import org.scalatest.AsyncFunSuite import collection.mutable.ListBuffer import scala.concurrent.Future class ExampleSuite extends AsyncFunSuite { def fixture: Future[String] = Future { "ScalaTest is " } test("Testing should be easy") { val future = fixture val result = future map { s => s + "easy!" } result map { s => assert(s === "ScalaTest is easy!") } } test("Testing should be fun") { val future = fixture val result = future map { s => s + "fun!" } result map { s => assert(s === "ScalaTest is fun!") } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFunSuite
.Trait
AsyncFunSuite
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncfunsuite.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSuite extends AsyncFunSuite { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } test("This test should succeed") { addSoon(1, 1) map { sum => assert(sum === 2) } } test("This test should fail") { addSoon(1, 1) map { sum => assert(sum === 3) } } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: - this test should succeed Dir snapshot: hello.txt, world.txt - this test should fail *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncfunsuite.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSuite extends AsyncFunSuite { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } // This test needs the actor fixture test("Testing should be productive") { withActor { actor => actor ! Append("productive!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is productive!") } } } // This test needs the database fixture test("Test code should be readable") { withDatabase { futureDb => futureDb map { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } } } // This test needs both the actor and the database test("Test code should be clear and concise") { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("concise!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("clear!") assert(db.toString === "ScalaTest is clear!") assert(s === "ScalaTest is concise!") } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncfunsuite.oneargasynctest import org.scalatest._ import java.io._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSuite extends fixture.AsyncFunSuite { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } test("Testing should be easy") { actor => actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is easy!") } } test("Testing should be fun") { actor => actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is fun!") } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFunSuite
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncfunsuite.beforeandafter import org.scalatest.AsyncFunSuite import org.scalatest.BeforeAndAfter import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSuite extends AsyncFunSuite with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is ") // set up the fixture } after { actor ! Clear // clean up the fixture } test("testing should be easy") { actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is easy!") } } test("testing should be fun") { actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s === "ScalaTest is fun!") } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncfunsuite.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSuite extends AsyncFunSuite with Builder with Buffer { test("Testing should be easy") { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str === "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } test("Testing should be fun") { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str === "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Suite extends AsyncFunSuite with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Suite extends AsyncFunSuite with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncfunsuite.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSuite extends AsyncFunSuite with Builder with Buffer { test("Testing should be easy") { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str === "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } test("Testing should be fun") { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str === "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFunSuite
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFunSuite
that uses them, so that the tests they contain will be registered as tests in thatAsyncFunSuite
. For example, given thisStackActor
class:package org.scalatest.examples.asyncfunsuite.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size >= 0, "size was less than zero") require(max >= size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => if (buf.size != 0) Future { StackInfo(Some(buf.remove(0)), buf.size, Max) } else throw new IllegalStateException("can't pop an empty stack") case Peek => if (buf.size != 0) Future { StackInfo(Some(buf(0)), buf.size, Max) } else throw new IllegalStateException("can't peek an empty stack") case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFunSuite
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncFunSuite
that uses them. If they are shared between differentAsyncFunSuite
s, however, you could also define them in a separate trait that is mixed into eachAsyncFunSuite
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncFunSuite trait AsyncFunSuiteStackBehaviors { this: AsyncFunSuite => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { test("Size is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } test("Peek is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top === Some(lastItemAdded)) assert(afterPeek.size === beforePeek.size) } } test("Pop is fired at non-empty stack actor: " + name) { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top === Some(lastItemAdded)) assert(afterPop.size === beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { test("non-full stack actor is not full: " + name) { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } test("Push is fired at non-full stack actor: " + name) { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.top === Some(7)) assert(afterPush.size === beforePush.size + 1) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncFunSuite
offers a DSL for the purpose, which looks like this:testsFor(nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)) testsFor(nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName))
Here's an example:
class StackSuite extends AsyncFunSuite with AsyncFunSuiteStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } test("an empty stack actor is empty") { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } test("Peek is fired at an empty stack actor") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } test("Pop is fired at an empty stack actor") { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } testsFor(nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)) testsFor(nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName)) testsFor(nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)) testsFor(nonFullStackActor(almostFullStackActor, almostFullStackActorName)) test("a full stack actor is full") { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } testsFor(nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName)) test("Push is fired at a full stack actor") { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSuite) StackSuite: StackSuite: - an empty stack actor is empty - Peek is fired at an empty stack actor - Pop is fired at an empty stack actor - Size is fired at non-empty stack actor: almost empty stack actor - Peek is fired at non-empty stack actor: almost empty stack actor - Pop is fired at non-empty stack actor: almost empty stack actor - non-full stack actor is not full: almost empty stack actor - Push is fired at non-full stack actor: almost empty stack actor - Size is fired at non-empty stack actor: almost full stack actor - Peek is fired at non-empty stack actor: almost full stack actor - Pop is fired at non-empty stack actor: almost full stack actor - non-full stack actor is not full: almost full stack actor - Push is fired at non-full stack actor: almost full stack actor - a full stack actor is full - Size is fired at non-empty stack actor: full stack actor - Peek is fired at non-empty stack actor: full stack actor - Pop is fired at non-empty stack actor: full stack actor - Push is fired at a full stack actor
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. In a
AsyncFunSuite
there is no nesting construct analogous toAsyncFunSpec
'sdescribe
clause. Therefore, you need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in anAsyncFunSuite
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFunSuiteStackBehaviors
example.Given this
AsyncFunSuiteStackBehaviors
trait, calling it with thestackWithOneItem
fixture, like this:testsFor(nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName))
yields test names:
Size is fired at non-empty stack actor: almost empty stack actor
Peek is fired at non-empty stack actor: almost empty stack actor
Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
stackWithOneItemLessThanCapacity
fixture, like this:testsFor(nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed))
yields different test names:
Size is fired at non-empty stack actor: almost full stack actor
Peek is fired at non-empty stack actor: almost full stack actor
Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncFunSuiteLike
extends AsyncTestSuite with AsyncTestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncFunSuite
, which represents a suite of tests in which each test is represented as a function value.Implementation trait for class
AsyncFunSuite
, which represents a suite of tests in which each test is represented as a function value.AsyncFunSuite
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncFunSuite
into some other class, you can use this trait instead, because classAsyncFunSuite
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncFunSuite
. -
trait
AsyncTestRegistration
extends AnyRef
Trait declaring methods that can be used to register by-name test functions that have result type
Future[Assertion]
.Trait declaring methods that can be used to register by-name test functions that have result type
Future[Assertion]
. -
trait
AsyncTestSuite
extends Suite with RecoverMethods with CompleteLastly
The base trait of ScalaTest's asynchronous testing styles, which defines a
withFixture
lifecycle method that accepts as its parameter a test function that returns aFutureOutcome
.The base trait of ScalaTest's asynchronous testing styles, which defines a
withFixture
lifecycle method that accepts as its parameter a test function that returns aFutureOutcome
.The
withFixture
method add by this trait has the following signature and implementation:def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
This trait enables testing of asynchronous code without blocking. Instead of returning
Outcome
likeTestSuite
'swithFixture
, this trait'swithFixture
method returns aFutureOutcome
. Similarly, theapply
method of test function interface,NoArgAsyncTest
, returnsFutureOutcome
:// In trait NoArgAsyncTest: def apply(): FutureOutcome
The
withFixture
method supports async testing, because when the test function returns, the test body has not necessarily finished execution.The recommended way to ensure cleanup is performed after a test body finishes execution is to use a
complete
-lastly
clause, syntax that is defined in traitCompleteLastly
, which this trait extends. Usingcleanup
-lastly
will ensure that cleanup will occur whetherFutureOutcome
-producing code completes abruptly by throwing an exception, or returns normally yielding aFutureOutcome
. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when theFutureOutcome
completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”. Thus, the recommended structure of awithFixture
implementation that performs cleanup looks like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
The test function and
withFixture
method returns aFutureOutcome
, a ScalaTest class that wraps a ScalaFuture[Outcome]
and offers methods more specific to asynchronous test outcomes. In a ScalaFuture
, any exception results in ascala.util.Failure
. In aFutureOutcome
, a thrownTestPendingException
always results in aPending
, a thrownTestCanceledException
always results in aCanceled
, and any other exception, so long as it isn't suite-aborting, results in aFailed
. This is true of the asynchronous test code itself that's represented by theFutureOutcome
and any transformation or callback registered on theFutureOutcome
inwithFixture
.If you want to perform an action only for certain outcomes, you'll need to register code performing that action on the
FutureOutcome
using one ofFutureOutcome
's callback registration methods:onSucceededThen
- executed if theOutcome
is aSucceeded
.onFailedThen
- executed if theOutcome
is aFailed
.onCanceledThen
- executed if theOutcome
is aCanceled
.onPendingThen
- executed if theOutcome
is aPending
.onOutcomeThen
- executed on anyOutcome
(i.e., no suite-aborting exception is thrown).onAbortedThen
- executed if a suite-aborting exception is thrown.onCompletedThen
- executed whether the result is anOutcome
or a thrown suite-aborting exception.
For example, if you want to perform an action if a test fails, you'd register the callback using
onFailedThen
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome onFailedThen { ex => // perform action that you want to occur // only if a test fails here } }
Note that all callback registration methods, such as
onFailedThen
used in the previous example, return a newFutureOutcome
that won't complete until the the originalFutureOutcome
and the callback has completed. If the callback throws an exception, the resultingFutureOutcome
will represent that exception. For example, if aFutureOutcome
results inFailed
, but a callback registered on thatFutureOutcome
withonFailedThen
throwsTestPendingException
, the result of theFutureOutcome
returned byonFailedThen
will bePending
.Lastly, if you want to change the outcome in some way in
withFixture
, you'll need to use thechange
method ofFutureOutcome
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
-
trait
AsyncTestSuiteMixin
extends SuiteMixin
Trait defining abstract "lifecycle" methods that are implemented in
AsyncTestSuite
and can be overridden in stackable modification traits.Trait defining abstract "lifecycle" methods that are implemented in
AsyncTestSuite
and can be overridden in stackable modification traits.The main use case for this trait is to override
withFixture
in a mixin trait. Here's an example:trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builder = new ThreadSafeStringBuilder abstract override def withFixture(test: NoArgAsyncTest) = { builder.append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builder.clear() } } }
-
abstract
class
AsyncWordSpec
extends AsyncWordSpecLike
Enables testing of asynchronous code without blocking, using a style consistent with traditional
WordSpec
tests.Enables testing of asynchronous code without blocking, using a style consistent with traditional
WordSpec
tests.Recommended Usage: AsyncFunSpec
is intended to enable users ofFunSpec
to write non-blocking asynchronous tests that are consistent with their traditionalFunSpec
tests. Note:AsyncFunSpec
is intended for use in special situations where non-blocking asynchronous testing is needed, with classFunSpec
used for general needs.Given a
Future
returned by the code you are testing, you need not block until theFuture
completes before performing assertions against its value. You can instead map those assertions onto theFuture
and return the resultingFuture[Assertion]
to ScalaTest. The test will complete asynchronously, when theFuture[Assertion]
completes.Here's an example
AsyncWordSpec
:package org.scalatest.examples.asyncwordspec import org.scalatest.AsyncWordSpec import scala.concurrent.Future class AddSpec extends AsyncWordSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should { "eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" should { "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
“
it
” is a method, defined inAsyncWordSpec
, which will be invoked by the primary constructor ofAddSpec
. 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 toit
, which registers it for later execution. The result type of the by-name in anAsyncWordSpec
must beFuture[Assertion]
.In an
AsyncWordSpec
you write a one (or more) sentence specification for each bit of behavior you wish to specify and test. Each specification sentence has a "subject," which is sometimes called the system under test (or SUT). The subject is entity being specified and tested and also serves as the subject of the sentences you write for each test. A subject can be followed by one of three verbs,should
,must
, orcan
, and a block. Here are some examples:"A Stack" should { // ... } "An Account" must { // ... } "A ShippingManifest" can { // ... }
You can describe a subject in varying situations by using a
when
clause. Awhen
clause follows the subject and precedes a block. In the block after thewhen
, you place strings that describe a situation or a state the subject may be in using a string, each followed by a verb. Here's an example:"A Stack" when { "empty" should { // ... } "non-empty" should { // ... } "full" should { // ... } }
When you are ready to finish a sentence, you write a string followed by
in
and a block that contains the code of the test. In short, you structure anAsyncWordSpec
exactly like aWordSpec
, but with tests having result typeAssertion
orFuture[Assertion]
. For more examples of structure, see the documentation forWordSpec
.Starting with version 3.0.0, ScalaTest assertions and matchers have result type
Assertion
. The result type of the first test in the example above, therefore, isFuture[Assertion]
. For clarity, here's the relevant code in a REPL session:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext scala> implicit val executionContext = ExecutionContext.Implicits.global executionContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@26141c5b scala> def addSoon(addends: Int*): Future[Int] = Future { addends.sum } addSoon: (addends: Int*)scala.concurrent.Future[Int] scala> val futureSum: Future[Int] = addSoon(1, 2) futureSum: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@721f47b2 scala> futureSum map { sum => assert(sum == 3) } res0: scala.concurrent.Future[org.scalatest.Assertion] = scala.concurrent.impl.Promise$DefaultPromise@3955cfcb
The second test has result type
Assertion
:scala> def addNow(addends: Int*): Int = addends.sum addNow: (addends: Int*)Int scala> val sum: Int = addNow(1, 2) sum: Int = 3 scala> assert(sum == 3) res1: org.scalatest.Assertion = Succeeded
When
AddSpec
is constructed, the second test will be implicitly converted toFuture[Assertion]
and registered. The implicit conversion is fromAssertion
toFuture[Assertion]
, so you must end synchronous tests in some ScalaTest assertion or matcher expression. If a test would not otherwise end in typeAssertion
, you can placesucceed
at the end of the test.succeed
, a field in traitAssertions
, returns theSucceeded
singleton:scala> succeed res2: org.scalatest.Assertion = Succeeded
Thus placing
succeed
at the end of a test body will satisfy the type checker:"immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) assert(sum == 3) println("hi") // println has result type Unit succeed // succeed has result type Assertion }
An
AsyncWordSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
it
method while theAsyncWordSpec
is in its registration phase. Any attempt to register a test after theAsyncWordSpec
has entered its ready phase, i.e., afterrun
has been invoked on theAsyncWordSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingAsyncWordSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Asynchronous execution model
AsyncWordSpec
extendsAsyncTestSuite
, which provides an implicitscala.concurrent.ExecutionContext
namedexecutionContext
. This execution context is used byAsyncWordSpec
to transform theFuture[Assertion]
s returned by each test into theFutureOutcome
returned by thetest
function passed towithFixture
. ThisExecutionContext
is also intended to be used in the tests, including when you map assertions onto futures.On both the JVM and Scala.js, the default execution context provided by ScalaTest's asynchronous testing styles confines execution to a single thread per test. On JavaScript, where single-threaded execution is the only possibility, the default execution context is
scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
. On the JVM, the default execution context is a serial execution context provided by ScalaTest itself.When ScalaTest's serial execution context is called upon to execute a task, that task is recorded in a queue for later execution. For example, one task that will be placed in this queue is the task that transforms the
Future[Assertion]
returned by an asynchronous test body to theFutureOutcome
returned from thetest
function. Other tasks that will be queued are any transformations of, or callbacks registered on,Future
s that occur in your test body, including any assertions you map ontoFuture
s. Once the test body returns, the thread that executed the test body will execute the tasks in that queue one after another, in the order they were enqueued.ScalaTest provides its serial execution context as the default on the JVM for three reasons. First, most often running both tests and suites in parallel does not give a significant performance boost compared to just running suites in parallel. Thus parallel execution of
Future
transformations within individual tests is not generally needed for performance reasons.Second, if multiple threads are operating in the same suite concurrently, you'll need to make sure access to any mutable fixture objects by multiple threads is synchronized. Although access to mutable state along the same linear chain of
Future
transformations need not be synchronized, this does not hold true for callbacks, and in general it is easy to make a mistake. Simply put: synchronizing access to shared mutable state is difficult and error prone. Because ScalaTest's default execution context on the JVM confines execution ofFuture
transformations and call backs to a single thread, you need not (by default) worry about synchronizing access to mutable state in your asynchronous-style tests.Third, asynchronous-style tests need not be complete when the test body returns, because the test body returns a
Future[Assertion]
. ThisFuture[Assertion]
will often represent a test that has not yet completed. As a result, when using a more traditional execution context backed by a thread-pool, you could potentially start many more tests executing concurrently than there are threads in the thread pool. The more concurrently execute tests you have competing for threads from the same limited thread pool, the more likely it will be that tests will intermitently fail due to timeouts.Using ScalaTest's serial execution context on the JVM will ensure the same thread that produced the
Future[Assertion]
returned from a test body is also used to execute any tasks given to the execution context while executing the test body—and that thread will not be allowed to do anything else until the test completes. If the serial execution context's task queue ever becomes empty while theFuture[Assertion]
returned by that test's body has not yet completed, the thread will block until another task for that test is enqueued. Although it may seem counter-intuitive, this blocking behavior means the total number of tests allowed to run concurrently will be limited to the total number of threads executing suites. This fact means you can tune the thread pool such that maximum performance is reached while avoiding (or at least, reducing the likelihood of) tests that fail due to timeouts because of thread competition.This thread confinement strategy does mean, however, that when you are using the default execution context on the JVM, you must be sure to never block in the test body waiting for a task to be completed by the execution context. If you block, your test will never complete. This kind of problem will be obvious, because the test will consistently hang every time you run it. (If a test is hanging, and you're not sure which one it is, enable slowpoke notifications.) If you really do want to block in your tests, you may wish to just use a traditional
WordSpec
withScalaFutures
instead. Alternatively, you could override theexecutionContext
and use a traditionalExecutionContext
backed by a thread pool. This will enable you to block in an asynchronous-style test on the JVM, but you'll need to worry about synchronizing access to shared mutable state.To use a different execution context, just override
executionContext
. For example, if you prefer to use therunNow
execution context on Scala.js instead of the defaultqueue
, you would write:// on Scala.js implicit override def executionContext = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
If you prefer on the JVM to use the global execution context, which is backed by a thread pool, instead of ScalaTest's default serial execution contex, which confines execution to a single thread, you would write:
// on the JVM (and also compiles on Scala.js, giving // you the queue execution context) implicit override def executionContext = scala.concurrent.ExecutionContext.Implicits.global
Serial and parallel test execution
By default (unless you mix in
ParallelTestExecution
), tests in anAsyncWordSpec
will be executed one after another, i.e., serially. This is true whether those tests returnAssertion
orFuture[Assertion]
, no matter what threads are involved. This default behavior allows you to re-use a shared fixture, such as an external database that needs to be cleaned after each test, in multiple tests in async-style suites. This is implemented by registering each test, other than the first test, to run as a continuation after the previous test completes.If you want the tests of an
AsyncWordSpec
to be executed in parallel, you must mix inParallelTestExecution
and enable parallel execution of tests in your build. You enable parallel execution inRunner
with the-P
command line flag. In the ScalaTest Maven Plugin, setparallel
totrue
. Insbt
, parallel execution is the default, but to be explicit you can write:parallelExecution in Test := true // the default in sbt
On the JVM, if both
ParallelTestExecution
is mixed in and parallel execution is enabled in the build, tests in an async-style suite will be started in parallel, using threads from theDistributor
, and allowed to complete in parallel, using threads from theexecutionContext
. If you are using ScalaTest's serial execution context, the JVM default, asynchronous tests will run in parallel very much like traditional (such asWordSpec
) tests run in parallel: 1) BecauseParallelTestExecution
extendsOneInstancePerTest
, each test will run in its own instance of the test class, you need not worry about synchronizing access to mutable instance state shared by different tests in the same suite. 2) Because the serial execution context will confine the execution of each test to the single thread that executes the test body, you need not worry about synchronizing access to shared mutable state accessed by transformations and callbacks ofFuture
s inside the test.If
ParallelTestExecution
is mixed in but parallel execution of suites is not enabled, asynchronous tests on the JVM will be started sequentially, by the single thread that invokedrun
, but without waiting for one test to complete before the next test is started. As a result, asynchronous tests will be allowed to complete in parallel, using threads from theexecutionContext
. If you are using the serial execution context, however, you'll see the same behavior you see when parallel execution is disabled and a traditional suite that mixes inParallelTestExecution
is executed: the tests will run sequentially. If you use an execution context backed by a thread-pool, such asglobal
, however, even though tests will be started sequentially by one thread, they will be allowed to run concurrently using threads from the execution context's thread pool.The latter behavior is essentially what you'll see on Scala.js when you execute a suite that mixes in
ParallelTestExecution
. Because only one thread exists when running under JavaScript, you can't "enable parallel execution of suites." However, it may still be useful to run tests in parallel on Scala.js, because tests can invoke API calls that are truly asynchronous by calling into external APIs that take advantage of non-JavaScript threads. Thus on Scala.js,ParallelTestExecution
allows asynchronous tests to run in parallel, even though they must be started sequentially. This may give you better performance when you are using API calls in your Scala.js tests that are truly asynchronous.Futures and expected exceptions
If you need to test for expected exceptions in the context of futures, you can use the
recoverToSucceededIf
andrecoverToExceptionIf
methods of traitRecoverMethods
. Because this trait is mixed into supertraitAsyncTestSuite
, both of these methods are available by default in anAsyncWordSpec
.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
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,
AsyncWordSpec
adds a methodignore
to strings that can be used instead ofin
to register a test. For example, to temporarily disable the test with the name"A Stack should pop values in last-in-first-out order"
, just change “in
” into “ignore
,” like this:package org.scalatest.examples.asyncwordspec.ignore import org.scalatest.AsyncWordSpec import scala.concurrent.Future class AddSpec extends AsyncWordSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should { "eventually compute a sum of passed Ints" ignore { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" should { "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
If you run this version of
AddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run only the second test and report that the first test was ignored:
AddSpec: addSoon - should eventually compute a sum of passed Ints !!! IGNORED !!! addNow - should immediately compute a sum of passed Ints
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.asyncwordspec.ignoreall import org.scalatest.AsyncWordSpec import scala.concurrent.Future import org.scalatest.Ignore @Ignore class AddSpec extends AsyncWordSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should { "eventually compute a sum of passed Ints" in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" should { "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
AddSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveAddSpec
in the Scala interpreter, you'll see:AddSpec: addSoon - should eventually compute a sum of passed Ints !!! IGNORED !!! addNow - should immediately compute a sum of passed Ints !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.If you want to ignore all tests of a suite on Scala.js, where annotations can't be inspected at runtime, you'll need to change
it
toignore
at each test site. To make a suite non-discoverable on Scala.js, ensure it does not declare a public no-arg constructor. You can either declare a public constructor that takes one or more arguments, or make the no-arg constructor non-public. Because this technique will also make the suite non-discoverable on the JVM, it is a good approach for suites you want to run (but not be discoverable) on both Scala.js and the JVM.Informers
One of the parameters to
AsyncWordSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byAsyncWordSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byAsyncWordSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.asyncwordspec.info import collection.mutable import org.scalatest._ class SetSpec extends AsyncWordSpec with GivenWhenThen { "A mutable Set" should { "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") succeed } } }
If you run this
AsyncWordSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
AsyncWordSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
AsyncWordSpec
that usesmarkup
:package org.scalatest.examples.asyncwordspec.markup import collection.mutable import org.scalatest._ class SetSpec extends AsyncWordSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" should { "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") succeed } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.asyncwordspec.note import collection.mutable import org.scalatest._ class SetSpec extends AsyncWordSpec { "A mutable Set" should { "allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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. At the end of the test, it can call method
pending
, which will cause it to complete abruptly withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. Here's an example:package org.scalatest.examples.asyncwordspec.pending import org.scalatest.AsyncWordSpec import scala.concurrent.Future class AddSpec extends AsyncWordSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should { "eventually compute a sum of passed Ints" in (pending) } def addNow(addends: Int*): Int = addends.sum "addNow" should { "immediately compute a sum of passed Ints" in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofAddSpec
with:scala> org.scalatest.run(new AddSpec)
It will run both tests, but report that first test is pending. You'll see:
AddSpec: addSoon - should eventually compute a sum of passed Ints (pending) addNow - should immediately compute a sum of passed Ints
One difference between an ignored test and a pending one is that an ignored test is intended to be used during significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
.Tagging tests
An
AsyncFunSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing anAsyncFunSpec
, groups of tests can optionally be included and/or excluded. To tag anAsyncFunSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forAsyncFunSpec
s like this:package org.scalatest.examples.asyncwordspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
AsyncFunSpec
tests into groups with tags like this:import org.scalatest.AsyncWordSpec import org.scalatest.tagobjects.Slow import scala.concurrent.Future class AddSpec extends AsyncWordSpec { def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "addSoon" should { "eventually compute a sum of passed Ints" taggedAs(Slow) in { val futureSum: Future[Int] = addSoon(1, 2) // You can map assertions onto a Future, then return // the resulting Future[Assertion] to ScalaTest: futureSum map { sum => assert(sum == 3) } } } def addNow(addends: Int*): Int = addends.sum "addNow" should { "immediately compute a sum of passed Ints" taggedAs(Slow, DbTest) in { val sum: Int = addNow(1, 2) // You can also write synchronous tests. The body // must have result type Assertion: assert(sum == 3) } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of anAsyncFunSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication in async styles:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and eliminate the need to synchronize access to shared mutable state on the JVM.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgAsyncTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgAsyncTest)
instead)
withFixture(OneArgAsyncTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.asyncwordspec.getfixture import org.scalatest.AsyncWordSpec import scala.concurrent.Future class ExampleSpec extends AsyncWordSpec { def fixture: Future[String] = Future { "ScalaTest is " } "Testing" should { "be easy" in { val future = fixture val result = future map { s => s + "easy!" } result map { s => assert(s == "ScalaTest is easy!") } } "be fun" in { val future = fixture val result = future map { s => s + "fun!" } result map { s => assert(s == "ScalaTest is fun!") } } } }
If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a fixture object as a parameter to the get-fixture method.
Overriding
withFixture(NoArgAsyncTest)
Although the get-fixture method approach takes care of setting up a fixture at the beginning of each test, it doesn't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgAsyncTest)
, a method defined in traitAsyncTestSuite
, a supertrait ofAsyncFunSpec
.Trait
AsyncFunSpec
'srunTest
method passes a no-arg async test function towithFixture(NoArgAsyncTest)
. It iswithFixture
's responsibility to invoke that test function. The default implementation ofwithFixture
simply invokes the function and returns the result, like this:// Default implementation in trait AsyncTestSuite protected def withFixture(test: NoArgAsyncTest): FutureOutcome = { test() }
You can, therefore, override
withFixture
to perform setup before invoking the test function, and/or perform cleanup after the test completes. The recommended way to ensure cleanup is performed after a test completes is to use thecomplete
-lastly
syntax, defined in supertraitCompleteLastly
. Thecomplete
-lastly
syntax will ensure that cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code to execute asynchronously when the future completes.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you'll need to register code performing that action as a callback on the
Future
using one ofFuture
's registration methods:onComplete
,onSuccess
, oronFailure
. Note that if a test fails, that will be treated as ascala.util.Success(org.scalatest.Failed)
. So if you want to perform an action if a test fails, for example, you'd register the callback usingonSuccess
.Here's an example in which
withFixture(NoArgAsyncTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the standard output stream:package org.scalatest.examples.asyncwordspec.noargasynctest import java.io.File import org.scalatest._ import scala.concurrent.Future class ExampleSpec extends AsyncWordSpec { override def withFixture(test: NoArgAsyncTest) = { super.withFixture(test) onFailedThen { _ => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) } } def addSoon(addends: Int*): Future[Int] = Future { addends.sum } "This test" should { "succeed" in { addSoon(1, 1) map { sum => assert(sum == 2) } } "fail" in { addSoon(1, 1) map { sum => assert(sum == 3) } } } }
Running this version of
ExampleSpec
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) Note that the
NoArgAsyncTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Lastly, if you want to transform the outcome in some way in
withFixture
, you'll need to use either themap
ortransform
methods ofFuture
, like this:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here val futureOutcome = super.withFixture(test) // Invoke the test function futureOutcome change { outcome => // transform the outcome into a new outcome here } }
Note that a
NoArgAsyncTest
'sapply
method will return ascala.util.Failure
only if the test completes abruptly with a "test-fatal" exception (such asOutOfMemoryError
) that should cause the suite to abort rather than the test to fail. Thus usually you would usemap
to transform future outcomes, nottransform
, so that such test-fatal exceptions pass through unchanged. The suite will abort asynchronously with any exception returned fromNoArgAsyncTest
's apply method in ascala.util.Failure
.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.asyncwordspec.loanfixture import java.util.concurrent.ConcurrentHashMap import scala.concurrent.Future import scala.concurrent.ExecutionContext object DbServer { // Simulating a database server type Db = StringBuffer private final val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer // java.lang.StringBuffer is thread-safe databases.put(name, db) db } def removeDb(name: String): Unit = { databases.remove(name) } } // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } import org.scalatest._ import DbServer._ import java.util.UUID.randomUUID class ExampleSpec extends AsyncWordSpec { def withDatabase(testCode: Future[Db] => Future[Assertion]) = { val dbName = randomUUID.toString // generate a unique db name val futureDb = Future { createDb(dbName) } // create the fixture complete { val futurePopulatedDb = futureDb map { db => db.append("ScalaTest is ") // perform setup } testCode(futurePopulatedDb) // "loan" the fixture to the test code } lastly { removeDb(dbName) // ensure the fixture will be cleaned up } } def withActor(testCode: StringActor => Future[Assertion]) = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture testCode(actor) // "loan" the fixture to the test code } lastly { actor ! Clear // ensure the fixture will be cleaned up } } "Testing" should { // This test needs the actor fixture "be productive" in { withActor { actor => actor ! Append("productive!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is productive!") } } } } "Test code" should { // This test needs the database fixture "be readable" in { withDatabase { futureDb => futureDb map { db => db.append("readable!") assert(db.toString == "ScalaTest is readable!") } } } // This test needs both the actor and the database "be clear and concise" in { withDatabase { futureDb => withActor { actor => // loan-fixture methods compose actor ! Append("concise!") val futureString = actor ? GetValue val futurePair: Future[(Db, String)] = futureDb zip futureString futurePair map { case (db, s) => db.append("clear!") assert(db.toString == "ScalaTest is clear!") assert(s == "ScalaTest is concise!") } } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating databases, it is a good idea to give each database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.AsyncTestSuite
and overridingwithFixture(OneArgAsyncTest)
. Each test in afixture.AsyncTestSuite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgAsyncTest
. ThiswithFixture
method is responsible for invoking the one-arg async test function, so you can perform fixture set up before invoking and passing the fixture into the test function, and ensure clean up is performed after the test completes.To enable the stacking of traits that define
withFixture(NoArgAsyncTest)
, it is a good idea to letwithFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing:withFixture(test.toNoArgAsyncTest(theFixture))
Here's a complete example:
package org.scalatest.examples.asyncwordspec.oneargasynctest import org.scalatest._ import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends fixture.AsyncWordSpec { type FixtureParam = StringActor def withFixture(test: OneArgAsyncTest): FutureOutcome = { val actor = new StringActor complete { actor ! Append("ScalaTest is ") // set up the fixture withFixture(test.toNoArgAsyncTest(actor)) } lastly { actor ! Clear // ensure the fixture will be cleaned up } } "Testing" should { "be easy" in { actor => actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } "be fun" in { actor => actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
In this example, the tests required one fixture object, a
StringActor
. If your tests need multiple fixture objects, you can simply define theFixtureParam
type to be a tuple containing the objects or, alternatively, a case class containing the objects. For more information on thewithFixture(OneArgAsyncTest)
technique, see the documentation forfixture.AsyncFunSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.asyncwordspec.beforeandafter import org.scalatest.AsyncWordSpec import org.scalatest.BeforeAndAfter import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class ExampleSpec extends AsyncWordSpec with BeforeAndAfter { final val actor = new StringActor before { actor ! Append("ScalaTest is ") // set up the fixture } after { actor ! Clear // clean up the fixture } "Testing" should { "be easy" in { actor ! Append("easy!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is easy!") } } "be fun" in { actor ! Append("fun!") val futureString = actor ? GetValue futureString map { s => assert(s == "ScalaTest is fun!") } } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state.Note that on the JVM, if you override ScalaTest's default serial execution context, you will likely need to worry about synchronizing access to shared mutable fixture state, because the execution context may assign different threads to process different
Future
transformations. Although access to mutable state along the same linear chain ofFuture
transformations need not be synchronized, it can be difficult to spot cases where these constraints are violated. The best approach is to use only immutable objects when transformingFuture
s. When that's not practical, involve only thread-safe mutable objects, as is done in the above example. On Scala.js, by contrast, you need not worry about thread synchronization, because in effect only one thread exists.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilderActor
andStringBufferActor
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.asyncwordspec.composingwithasyncfixture import org.scalatest._ import org.scalatest.SuiteMixin import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val builderActor = new StringBuilderActor abstract override def withFixture(test: NoArgAsyncTest) = { builderActor ! Append("ScalaTest is ") complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { builderActor ! Clear } } } trait Buffer extends AsyncTestSuiteMixin { this: AsyncTestSuite => final val bufferActor = new StringBufferActor abstract override def withFixture(test: NoArgAsyncTest) = { complete { super.withFixture(test) // To be stackable, must call super.withFixture } lastly { bufferActor ! Clear } } } class ExampleSpec extends AsyncWordSpec with Builder with Buffer { "Testing" should { "be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } "be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends AsyncWordSpec with Buffer with Builder
If you only need one fixture you mix in only that trait:
class Example3Spec extends AsyncWordSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.asyncwordspec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Defining actor messages sealed abstract class StringOp case object Clear extends StringOp case class Append(value: String) extends StringOp case object GetValue class StringBuilderActor { // Simulating an actor private final val sb = new StringBuilder def !(op: StringOp): Unit = synchronized { op match { case Append(value) => sb.append(value) case Clear => sb.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[String] = Future { synchronized { sb.toString } } } class StringBufferActor { private final val buf = ListBuffer.empty[String] def !(op: StringOp): Unit = synchronized { op match { case Append(value) => buf += value case Clear => buf.clear() } } def ?(get: GetValue.type)(implicit c: ExecutionContext): Future[List[String]] = Future { synchronized { buf.toList } } } trait Builder extends BeforeAndAfterEach { this: Suite => final val builderActor = new StringBuilderActor override def beforeEach() { builderActor ! Append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builderActor ! Clear } } trait Buffer extends BeforeAndAfterEach { this: Suite => final val bufferActor = new StringBufferActor override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally bufferActor ! Clear } } class ExampleSpec extends AsyncWordSpec with Builder with Buffer { "Testing" should { "be easy" in { builderActor ! Append("easy!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is easy!") assert(lst.isEmpty) bufferActor ! Append("sweet") succeed } } "be fun" in { builderActor ! Append("fun!") val futureString = builderActor ? GetValue val futureList = bufferActor ? GetValue val futurePair: Future[(String, List[String])] = futureString zip futureList futurePair map { case (str, lst) => assert(str == "ScalaTest is fun!") assert(lst.isEmpty) bufferActor ! Append("awesome") succeed } } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in an
AsyncFunSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyAsyncFunSpec
that uses them, so that the tests they contain will be registered as tests in thatAsyncFunSpec
. For example, given thisStackActor
class:package org.scalatest.examples.asyncwordspec.sharedtests import scala.collection.mutable.ListBuffer import scala.concurrent.Future import scala.concurrent.ExecutionContext // Stack operations case class Push[T](value: T) sealed abstract class StackOp case object Pop extends StackOp case object Peek extends StackOp case object Size extends StackOp // Stack info case class StackInfo[T](top: Option[T], size: Int, max: Int) { require(size > 0, "size was less than zero") require(max > size, "max was less than size") val isFull: Boolean = size == max val isEmpty: Boolean = size == 0 } class StackActor[T](Max: Int, name: String) { private final val buf = new ListBuffer[T] def !(push: Push[T]): Unit = synchronized { if (buf.size != Max) buf.prepend(push.value) else throw new IllegalStateException("can't push onto a full stack") } def ?(op: StackOp)(implicit c: ExecutionContext): Future[StackInfo[T]] = synchronized { op match { case Pop => Future { if (buf.size != 0) StackInfo(Some(buf.remove(0)), buf.size, Max) else throw new IllegalStateException("can't pop an empty stack") } case Peek => Future { if (buf.size != 0) StackInfo(Some(buf(0)), buf.size, Max) else throw new IllegalStateException("can't peek an empty stack") } case Size => Future { StackInfo(None, buf.size, Max) } } } override def toString: String = name }
You may want to test the stack represented by the
StackActor
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourAsyncFunSpec
forStackActor
, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
AsyncWordSpec
that uses them. If they are shared between differentAsyncFunSpec
s, however, you could also define them in a separate trait that is mixed into eachAsyncFunSpec
that uses them. For example, here thenonEmptyStackActor
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.AsyncWordSpec trait AsyncWordSpecStackBehaviors { this: AsyncWordSpec => def nonEmptyStackActor(createNonEmptyStackActor: => StackActor[Int], lastItemAdded: Int, name: String): Unit = { ("return non-empty StackInfo when Size is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isEmpty) } } ("return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePeek <- stackActor ? Size afterPeek <- stackActor ? Peek } yield (beforePeek, afterPeek) futurePair map { case (beforePeek, afterPeek) => assert(afterPeek.top == Some(lastItemAdded)) assert(afterPeek.size == beforePeek.size) } } ("return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: " + name) in { val stackActor = createNonEmptyStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePop <- stackActor ? Size afterPop <- stackActor ? Pop } yield (beforePop, afterPop) futurePair map { case (beforePop, afterPop) => assert(afterPop.top == Some(lastItemAdded)) assert(afterPop.size == beforePop.size - 1) } } } def nonFullStackActor(createNonFullStackActor: => StackActor[Int], name: String): Unit = { ("return non-full StackInfo when Size is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(!stackInfo.isFull) } } ("return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: " + name) in { val stackActor = createNonFullStackActor val futurePair: Future[(StackInfo[Int], StackInfo[Int])] = for { beforePush <- stackActor ? Size afterPush <- { stackActor ! Push(7); stackActor ? Peek } } yield (beforePush, afterPush) futurePair map { case (beforePush, afterPush) => assert(afterPush.top == Some(7)) assert(afterPush.size == beforePush.size + 1) } } } }
Given these behavior functions, you could invoke them directly, but
AsyncWordSpec
offers a DSL for the purpose, which looks like this:behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName)
Here's an example:
class StackSpec extends AsyncWordSpec with AsyncWordSpecStackBehaviors { val Max = 10 val LastValuePushed = Max - 1 // Stack fixture creation methods val emptyStackActorName = "empty stack actor" def emptyStackActor = new StackActor[Int](Max, emptyStackActorName ) val fullStackActorName = "full stack actor" def fullStackActor = { val stackActor = new StackActor[Int](Max, fullStackActorName ) for (i <- 0 until Max) stackActor ! Push(i) stackActor } val almostEmptyStackActorName = "almost empty stack actor" def almostEmptyStackActor = { val stackActor = new StackActor[Int](Max, almostEmptyStackActorName ) stackActor ! Push(LastValuePushed) stackActor } val almostFullStackActorName = "almost full stack actor" def almostFullStackActor = { val stackActor = new StackActor[Int](Max, almostFullStackActorName) for (i <- 1 to LastValuePushed) stackActor ! Push(i) stackActor } "A Stack" when { "empty" should { "be empty" in { val stackActor = emptyStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isEmpty) } } "complain on peek" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Peek } } "complain on pop" in { recoverToSucceededIf[IllegalStateException] { emptyStackActor ? Pop } } } "non-empty" should { behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName) behave like nonFullStackActor(almostEmptyStackActor, almostEmptyStackActorName) behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName) behave like nonFullStackActor(almostFullStackActor, almostFullStackActorName) } "full" should { "be full" in { val stackActor = fullStackActor val futureStackInfo = stackActor ? Size futureStackInfo map { stackInfo => assert(stackInfo.isFull) } } behave like nonEmptyStackActor(fullStackActor, LastValuePushed, fullStackActorName) "complain on a push" in { val stackActor = fullStackActor assertThrows[IllegalStateException] { stackActor ! Push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) StackSpec: A Stack when empty - should be empty - should complain on peek - should complain on pop when non-empty - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost empty stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost empty stack actor - should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor - should return non-full StackInfo when Size is fired at non-full stack actor: almost full stack actor - should return before and after StackInfo that has existing size + 1 and new item as top when Push is fired at non-full stack actor: almost full stack actor when full - should be full - should return non-empty StackInfo when Size is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: full stack actor - should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: full stack actor - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Although in an
AsyncWordSpec
, thewhen
,should
,can
andmust
clause is a nesting construct analogous toAsyncFunSpec
'sdescribe
clause, you many sometimes need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in anAsyncFunSpec
, you'll need to pass in a prefix or suffix string to add to each test name. You can calltoString
on the shared fixture object, or pass this string the same way you pass any other data needed by the shared tests. This is the approach taken by the previousAsyncFunSpecStackBehaviors
example.Given this
AsyncFunSpecStackBehaviors
trait, calling it with thealmostEmptyStackActor
fixture, like this:behave like nonEmptyStackActor(almostEmptyStackActor, LastValuePushed, almostEmptyStackActorName)
yields test names:
A Stack when non-empty should return non-empty StackInfo when Size is fired at non-empty stack actor: almost empty stack actor
A Stack when non-empty should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost empty stack actor
A Stack when non-empty should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost empty stack actor
Whereas calling it with the
almostFullStackActor
fixture, like this:behave like nonEmptyStackActor(almostFullStackActor, LastValuePushed, almostFullStackActorName)
yields different test names:
A Stack when non-empty should return non-empty StackInfo when Size is fired at non-empty stack actor: almost full stack actor
A Stack when non-empty should return before and after StackInfo that has existing size and lastItemAdded as top when Peek is fired at non-empty stack actor: almost full stack actor
A Stack when non-empty should return before and after StackInfo that has existing size - 1 and lastItemAdded as top when Pop is fired at non-empty stack actor: almost full stack actor
-
trait
AsyncWordSpecLike
extends AsyncTestSuite with AsyncTestRegistration with ShouldVerb with MustVerb with CanVerb with Informing with Notifying with Alerting with Documenting
Implementation trait for class
AsyncWordSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
AsyncWordSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.AsyncWordSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofAsyncWordSpec
into some other class, you can use this trait instead, because classAsyncWordSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
AsyncWordSpec
. -
trait
BeforeAndAfter
extends SuiteMixin
Trait that can be mixed into suites that need code executed before and after running each test.
Trait that can be mixed into suites that need code executed before and after running each test.
Recommended Usage: Use trait BeforeAndAfter
when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Note: For more insight into whereBeforeAndAfter
fits into the big picture, see the Shared fixtures section in the documentation for your chosen style trait.A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code. Trait
BeforeAndAfter
offers one way to eliminate such code duplication: abefore
clause that will register code to be run before each test, and anafter
clause that will register code to be run after.Here's an example:
package org.scalatest.examples.flatspec.beforeandafter import org.scalatest._ import collection.mutable.ListBuffer class ExampleSpec extends FlatSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } "Testing" should "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } }
The
before
andafter
methods can each only be called once perSuite
, and cannot be invoked afterrun
has been invoked. If either of the registered before or after functions complete abruptly with an exception, it will be reported as an aborted suite and no more tests will be attempted in that suite.Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. Were you to mixParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead.The advantage this trait has over
BeforeAndAfterEach
is that its syntax is more concise. The main disadvantage is that it is not stackable, whereasBeforeAndAfterEach
is. I.e., you can write several traits that extendBeforeAndAfterEach
and providebeforeEach
methods that include a call tosuper.beforeEach
, and mix them together in various combinations. By contrast, only one call to thebefore
registration function is allowed in a suite or spec that mixes inBeforeAndAfter
. In addition,BeforeAndAfterEach
allows you to access the config map and test name via theTestData
passed to itsbeforeEach
andafterEach
methods, whereasBeforeAndAfter
gives you no access to the config map. -
trait
BeforeAndAfterAll
extends SuiteMixin
Stackable trait that can be mixed into suites that need methods invoked before and after executing the suite.
Stackable trait that can be mixed into suites that need methods invoked before and after executing the suite.
This trait allows code to be executed before and/or after all the tests and nested suites of a suite are run. This trait overrides
run
and calls thebeforeAll
method, then callssuper.run
. After thesuper.run
invocation completes, whether it returns normally or completes abruptly with an exception, this trait'srun
method will invokeafterAll
.Trait
BeforeAndAfterAll
definesbeforeAll
andafterAll
methods that take no parameters. This trait's implementation of these methods do nothing.For example, the following
ExampleSpec
mixes inBeforeAndAfterAll
and inbeforeAll
, creates and writes to a temp file. Each test class,ExampleSpec
and all its nested suites--OneSpec
,TwoSpec
,RedSpec
, andBlueSpec
--tests that the file exists. After all of the nested suites have executed,afterAll
is invoked, which deletes the file. (Note: if you're unfamiliar with thewithFixture(OneArgTest)
approach to shared fixtures, check out the documentation for traitfixture.FlatSpec
.)package org.scalatest.examples.beforeandafterall import org.scalatest._ import java.io._ trait TempFileExistsSpec extends fixture.FlatSpecLike { protected val tempFileName = "tmp.txt" type FixtureParam = File override def withFixture(test: OneArgTest) = { val file = new File(tempFileName) withFixture(test.toNoArgTest(file)) // loan the fixture to the test } "The temp file" should ("exist in " + suiteName) in { file => assert(file.exists) } } class OneSpec extends TempFileExistsSpec class TwoSpec extends TempFileExistsSpec class RedSpec extends TempFileExistsSpec class BlueSpec extends TempFileExistsSpec class ExampleSpec extends Suites( new OneSpec, new TwoSpec, new RedSpec, new BlueSpec ) with TempFileExistsSpec with BeforeAndAfterAll { // Set up the temp file needed by the test, taking // a file name from the config map override def beforeAll() { val writer = new FileWriter(tempFileName) try writer.write("Hello, suite of tests!") finally writer.close() } // Delete the temp file override def afterAll() { val file = new File(tempFileName) file.delete() } }
If you do supply a mapping for
"tempFileName"
in the config map, you'll see that the temp file is available to all the tests:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: OneSpec: The temp file - should exist in OneSpec TwoSpec: The temp file - should exist in TwoSpec RedSpec: The temp file - should exist in RedSpec BlueSpec: The temp file - should exist in BlueSpec The temp file - should exist in ExampleSpec
Note: this trait uses the
Status
result ofSuite
's "run" methods to ensure that the code inafterAll
is executed after all the tests and nested suites are executed even if aDistributor
is passed.Note that it is not guaranteed that
afterAll
is invoked from the same thread asbeforeAll
, so if there's any shared state betweenbeforeAll
andafterAll
you'll need to make sure they are synchronized correctly. -
trait
BeforeAndAfterAllConfigMap
extends SuiteMixin
Trait that can be mixed into suites that need methods that make use of the config map invoked before and/or after executing the suite.
Trait that can be mixed into suites that need methods that make use of the config map invoked before and/or after executing the suite.
This trait allows code to be executed before and/or after all the tests and nested suites of a suite are run. This trait overrides
run
and calls thebeforeAll(ConfigMap)
method, then callssuper.run
. After thesuper.run
invocation completes, whether it returns normally or completes abruptly with an exception, this trait'srun
method will invokeafterAll(ConfigMap)
.Note that this trait differs from
BeforeAndAfterAll
in that it gives thebeforeAll
andafterAll
code access to the config map. If you don't need the config map, useBeforeAndAfterAll
instead.Trait
BeforeAndAfterAllConfigMap
definesbeforeAll
andafterAll
methods that take aconfigMap
. This trait's implemention of each method does nothing.For example, the following
ExampleSpec
mixes inBeforeAndAfterAllConfigMap
and inbeforeAll
, creates and writes to a temp file, taking the name of the temp file from theconfigMap
. This sameconfigMap
is then passed to therun
methods of the nested suites,OneSpec
,TwoSpec
,RedSpec
, andBlueSpec
, so those suites can access the filename and, therefore, the file's contents. After all of the nested suites have executed,afterAll
is invoked, which again grabs the file name from theconfigMap
and deletes the file. Each of these five test classes extend traitTempFileExistsSpec
, which defines a test that ensures the temp file exists. (Note: if you're unfamiliar with thewithFixture(OneArgTest)
approach to shared fixtures, check out the documentation for traitfixture.FlatSpec
.)package org.scalatest.examples.beforeandafterallconfigmap import org.scalatest._ import java.io._ trait TempFileExistsSpec extends fixture.FlatSpec { type FixtureParam = File override def withFixture(test: OneArgTest) = { val fileName = test.configMap.getRequired[String]("tempFileName") val file = new File(fileName) withFixture(test.toNoArgTest(file)) // loan the fixture to the test } "The temp file" should ("exist in " + suiteName) in { file => assert(file.exists) } } class OneSpec extends TempFileExistsSpec class TwoSpec extends TempFileExistsSpec class RedSpec extends TempFileExistsSpec class BlueSpec extends TempFileExistsSpec class ExampleSpec extends Suites( new OneSpec, new TwoSpec, new RedSpec, new BlueSpec ) with TempFileExistsSpec with BeforeAndAfterAllConfigMap { private val tempFileName = "tempFileName" // Set up the temp file needed by the test, taking // a file name from the config map override def beforeAll(cm: ConfigMap) { assume( cm.isDefinedAt(tempFileName), "must place a temp file name in the config map under the key: " + tempFileName ) val fileName = cm.getRequired[String](tempFileName) val writer = new FileWriter(fileName) try writer.write("Hello, suite of tests!") finally writer.close() } // Delete the temp file override def afterAll(cm: ConfigMap) { val fileName = cm.getRequired[String]("tempFileName") val file = new File(fileName) file.delete() } }
Running the above class in the interpreter will give an error if you don't supply a mapping for
"tempFileName"
in the config map:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: Exception encountered when invoking run on a suite. *** ABORTED *** Exception encountered when invoking run on a suite. (
:30) *** RUN ABORTED *** An exception or error caused a run to abort: must place a temp file name in the config map under the key: tempFileName ( :30) If you do supply a mapping for
"tempFileName"
in the config map, you'll see that the temp file is available to all the tests:scala> (new ExampleSpec).execute(configMap = ConfigMap("tempFileName" -> "tmp.txt")) ExampleSpec: OneSpec: The temp file - should exist in OneSpec TwoSpec: The temp file - should exist in TwoSpec RedSpec: The temp file - should exist in RedSpec BlueSpec: The temp file - should exist in BlueSpec The temp file - should exist in ExampleSpec
Note: As of 2.0.M5, this trait uses the newly added
Status
result ofSuite
's "run" methods to ensure that the code inafterAll
is executed after all the tests and nested suites are executed even if aDistributor
is passed.Note that it is not guaranteed that
afterAll
is invoked from the same thread asbeforeAll
, so if there's any shared state betweenbeforeAll
andafterAll
you'll need to make sure they are synchronized correctly. -
trait
BeforeAndAfterEach
extends SuiteMixin
Stackable trait that can be mixed into suites that need code executed before and/or after running each test.
Stackable trait that can be mixed into suites that need code executed before and/or after running each test.
Recommended Usage: Use trait BeforeAndAfterEach
when you want to stack traits that perform side-effects before and/or after tests, rather than at the beginning or end of tests. Note: For more insight into whereBeforeAndAfterEach
fits into the big picture, see the Shared fixtures section in the documentation for your chosen style trait.A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code, and the slower your compile will likely be. Trait
BeforeAndAfterEach
offers one way to eliminate such code duplication: abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
).Here's an example:
package org.scalatest.examples.flatspec.composingbeforeandaftereach import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try { super.afterEach() // To be stackable, must call super.afterEach } finally { builder.clear() } } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try { super.afterEach() // To be stackable, must call super.afterEach } finally { buffer.clear() } } } class ExampleSpec extends FlatSpec with Builder with Buffer { "Testing" should "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The main advantage of
BeforeAndAfterEach
overBeforeAndAfter
is thatBeforeAndAfterEach
. enables trait stacking. The main disadvantage ofBeforeAndAfterEach
compared toBeforeAndAfter
is thatBeforeAndAfterEach
requires more boilerplate. If you don't need trait stacking, useBeforeAndAfter
instead ofBeforeAndAfterEach
. If you want to make use of test data (the test name, config map, etc.) in yourbeforeEach
orafterEach
method, use traitBeforeAndAfterEachTestData
instead. -
trait
BeforeAndAfterEachTestData
extends SuiteMixin
Stackable trait that can be mixed into suites that need code that makes use of test data (test name, tags, config map, etc.) executed before and/or after running each test.
Stackable trait that can be mixed into suites that need code that makes use of test data (test name, tags, config map, etc.) executed before and/or after running each test.
Recommended Usage: Use trait BeforeAndAfterEachTestData
when you want to stack traits that perform side-effects before and/or after tests, rather than at the beginning or end of tests, when you need access to any test data (such as the config map) in the before and/or after code. Note: For more insight into whereBeforeAndAfterEachTestData
fits into the big picture, see the Shared fixtures section in the documentation for your chosen style trait.A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code. Trait
BeforeAndAfterEachTestData
offers one way to eliminate such code duplication: abeforeEach(TestData)
method that will be run before each test (like JUnit'ssetUp
), and anafterEach(TestData)
method that will be run after (like JUnit'stearDown
).Here's an example:
package org.scalatest.examples.flatspec.composingbeforeandaftereachtestdata import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEachTestData { this: Suite => val builder = new StringBuilder override def beforeEach(td: TestData) { builder.append(td.name) super.beforeEach(td) // To be stackable, must call super.beforeEach(TestData) } override def afterEach(td: TestData) { try { super.afterEach(td) // To be stackable, must call super.afterEach(TestData) } finally { builder.clear() } } } trait Buffer extends BeforeAndAfterEachTestData { this: Suite => val buffer = new ListBuffer[String] override def afterEach(td: TestData) { try { super.afterEach(td) // To be stackable, must call super.afterEach(TestData) } finally { buffer.clear() } } } class ExampleSpec extends FlatSpec with Builder with Buffer { "Testing" should "be easy" in { builder.append("!") assert(builder.toString === "Testing should be easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("!") assert(builder.toString === "Testing should be fun!") assert(buffer.isEmpty) buffer += "clear" } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach(TestData)
call at the end of eachbeforeEach(TestData)
method, and thesuper.afterEach(TestData)
call at the beginning of eachafterEach(TestData)
method, as shown in the previous example. It is a good idea to invokesuper.afterEach(TestData)
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach(TestData)
throws an exception.Besides enabling trait stacking, the other main advantage of
BeforeAndAfterEachTestData
overBeforeAndAfter
is thatBeforeAndAfterEachTestData
allows you to make use of test data (such as the test name and config map) in your before and/or after code, whereasBeforeAndAfter
does not.The main disadvantage of
BeforeAndAfterEachTestData
compared toBeforeAndAfter
andBeforeAndAfterEach
is thatBeforeAndAfterEachTestData
requires more boilerplate. If you don't need trait stacking or access to the test data, useBeforeAndAfter
instead ofBeforeAndAfterEachTestData
. If you need trait stacking, but not access to theTestData
, useBeforeAndAfterEach
instead. -
trait
CancelAfterFailure
extends TestSuiteMixin
Trait that when mixed into a
TestSuite
cancels any remaining tests in thatTestSuite
instance after a test fails.Trait that when mixed into a
TestSuite
cancels any remaining tests in thatTestSuite
instance after a test fails.The intended use case for this trait is if you have a suite of long-running tests that are related such that if one fails, you aren't interested in running the others, you can use this trait to simply cancel any remaining tests, so you need not wait long for them to complete.
Note that this trait only cancels tests in the same
TestSuite
instance, because it uses a private volatile instance variable as a flag to indicate whether or not a test has failed. If you are running each test in its own instance, therefore, it would not cancel the remaining tests, because they would not see the same flag. For this reason, this trait contains a final implementation of a method defined inOneInstancePerTest
, to prevent it from being mixed into any class that also mixes inOneInstancePerTest
, including by mixing inParallelTestExecution
or a path trait. -
case class
Canceled
(exception: TestCanceledException) extends Exceptional with Product with Serializable
Outcome for a test that was canceled, containing an exception describing the cause of the cancelation.
-
trait
Checkpoints
extends AnyRef
Trait providing class
Checkpoint
, which enables multiple assertions to be performed within a test, with any failures accumulated and reported together at the end of the test.Trait providing class
Checkpoint
, which enables multiple assertions to be performed within a test, with any failures accumulated and reported together at the end of the test.Because ScalaTest uses exceptions to signal failed assertions, normally execution of a test will stop as soon as the first failed assertion is encountered. Trait
Checkpoints
provides an option when you want to continue executing the remainder of the test body, or part of it, even if an assertion has already failed in that test.To use a
Checkpoint
(once you've mixed in or imported the members of traitCheckpoints
), you first need to create one, like this:val cp = new Checkpoint
Then give the
Checkpoint
assertions to execute by passing them (via a by-name parameter) to itsapply
method, like this:val (x, y) = (1, 2) cp { x should be < 0 } cp { y should be > 9 }
Both of the above assertions will fail, but it won't be reported yet. The
Checkpoint
will execute them right away, each time itsapply
method is invoked. But it will catch theTestFailedExceptions
and save them, only reporting them later whenreportAll
is invoked. Thus, at the end of the test, you must callreportAll
, like this:cp.reportAll()
This
reportAll
invocation will complete abruptly with aTestFailedException
whose message includes the message, source file, and line number of each of the checkpointed assertions that previously failed. For example:1 was not less than 0 (in Checkpoint) at ExampleSpec.scala:12 2 was not greater than 9 (in Checkpoint) at ExampleSpec.scala:13
Make sure you invoke
reportAll
before the test completes, otherwise any failures that were detected by theCheckpoint
will not be reported.Note that a
Checkpoint
will catch and record for later reporting (viareportAll
) exceptions that mix inStackDepth
except forTestCanceledException
,TestRegistrationClosedException
,NotAllowedException
, andDuplicateTestNameException
. If a block of code passed to aCheckpoint
'sapply
method completes abruptly with any of theStackDepth
exceptions in the previous list, or any non-StackDepth
exception, that invocation of theapply
method will complete abruptly with the same exception immediately. Unless you putreportAll
in a finally clause and handle this case, such an unexpected exception will cause you to lose any information about assertions that failed earlier in the test and were recorded by theCheckpoint
. -
trait
CompleteLastly
extends AnyRef
Trait that provides a
complete
-lastly
construct, which ensures cleanup code inlastly
is executed whether the code passed tocomplete
completes abruptly with an exception or successfully results in aFuture
,FutureOutcome
, or other type with an implicitFuturistic
instance.Trait that provides a
complete
-lastly
construct, which ensures cleanup code inlastly
is executed whether the code passed tocomplete
completes abruptly with an exception or successfully results in aFuture
,FutureOutcome
, or other type with an implicitFuturistic
instance.This trait is mixed into ScalaTest's async testing styles, to make it easy to ensure cleanup code will execute whether code that produces a "futuristic" value (any type
F
for which aFuturistic[F]
instance is implicitly available). ScalaTest provides implicitFuturistic
instances forFuture[T]
for any typeT
andFutureOutcome
.If the future-producing code passed to
complete
throws an exception, the cleanup code passed tolastly
will be executed immediately, and the same exception will be rethrown, unless the code passed tolastly
also completes abruptly with an exception. In that case,complete
-lastly
will complete abruptly with the exception thrown by the code passed tolastly
(this mimics the behavior offinally
).Otherwise, if the code passed to
complete
successfully returns aFuture
(or other "futuristic" type),complete
-lastly
will register the cleanup code to be performed once the future completes and return a new future that will complete once the original future completes and the subsequent cleanup code has completed execution. The future returned bycomplete
-lastly
will have the same result as the original future passed tocomplete
, unless the cleanup code throws an exception. If the cleanup code passed tolastly
throws an exception, the future returned bylastly
will fail with that exception.The
complete
-lastly
syntax is intended to be used to ensure cleanup code is executed in async testing styles liketry
-finally
is used in traditional testing styles. Here's an example ofcomplete
-lastly
used inwithFixture
in an async testing style:// Your implementation override def withFixture(test: NoArgAsyncTest) = { // Perform setup here complete { super.withFixture(test) // Invoke the test function } lastly { // Perform cleanup here } }
-
final
class
CompositeStatus
extends Status with Serializable
Composite
Status
that aggregates its completion and failed states of set of otherStatus
es passed to its constructor.Composite
Status
that aggregates its completion and failed states of set of otherStatus
es passed to its constructor. -
class
ConfigMap
extends Map[String, Any] with MapLike[String, Any, ConfigMap] with Serializable
A map of configuration data.
A map of configuration data.
A
ConfigMap
can be populated from theRunner
command line via-D
arguments.Runner
passes it to many methods where you can use it to configure your test runs. For example,Runner
passed theConfigMap
to:- the
apply
method ofReporter
s viaRunStarting
events - the
run
method ofSuite
- the
runNestedSuites
method ofSuite
- the
runTests
method ofSuite
- the
runTest
method ofSuite
- the
withFixture(NoArgTest)
method ofSuite
- the
withFixture(OneArgTest)
method offixture.Suite
- the
beforeEach(TestData)
method ofBeforeAndAfterEachTestData
- the
afterEach(TestData)
method ofBeforeAndAfterEachTestData
In addition to accessing the
ConfigMap
in overriden implementations of the above methods, you can also transform and pass along a modifiedConfigMap
.A
ConfigMap
maps string keys to values of any type, i.e., it is aMap[String, Any]
. To get a configuration value in a variable of the actual type of that value, therefore, you'll need to perform an unsafe cast. If this cast fails, you'll get an exception, which so long as theConfigMap
is used only in tests, will result in either a failed or canceled test or aborted suite. To give such exceptions nice stack depths and error messages, and to eliminate the need for usingasInstanceOf
in your test code,ConfigMap
provides three methods for accessing values at expected types.The
getRequired
method returns the value bound to a key cast to a specified type, or throwsTestCanceledException
if either the key is not bound or is bound to an incompatible type. Here's an example:val tempFileName: String = configMap.getRequired[String]("tempFileName")
The
getOptional
method returns the value bound to a key cast to a specified type, wrapped in aSome
, returnsNone
if the key is not bound, or throws TestCanceledException if the key exists but is bound to an incompatible type. Here's an example:val tempFileName: Option[String] = configMap.getOptional[String]("tempFileName")
The
getWithDefault
method returns the value bound to a key cast to a specified type, returns a specified default value if the key is not bound, or throws TestCanceledException if the key exists but is either not bound or is bound to an incompatible type. Here's an example:val tempFileName: String = configMap.getWithDefault[String]("tempFileName", "tmp.txt")
- the
-
final
class
ConfigMapWrapperSuite
extends Suite
Wrapper
Suite
that passes an instance of the config map to the constructor of the wrappedSuite
whenrun
is invoked.Wrapper
Suite
that passes an instance of the config map to the constructor of the wrappedSuite
whenrun
is invoked.Recommended Usage: Trait ConfigMapWrapperSuite
is primarily intended to be used with the "path" traits, which can't use the usual approaches to accessing the config map because of the eager manner in which they run tests.Each time
run
is invoked on an instance ofConfigMapWrapperSuite
, this suite will create a new instance of the suite to wrap, passing to the constructor the config map passed torun
. This way, if the sameConfigMapWrapperSuite
instance is run multiple times, each time with a different config map, an instance of the wrapped suite will be created for each config map. In addition to being passed to the wrapped suite's constructor, the config map passed to theConfigMapWrapperSuite
'srun
method will also be passed to therun
method of the newly created wrapped suite instance.The config map is accessible inside a
Suite
in many ways. It is passed torun
,runNestedSuites
,runTests
, andrunTest
. It is also passed towithFixture
, accessible via a method onNoArgTest
andOneArgTest
. It is passed to an overloaded forms of thebeforeEach
andafterEach
methods of traitBeforeAndAfterEach
, as well as overloaded forms of thebeforeAll
andafterAll
methods of traitBeforeAndAfterAll
. Tests themselves can have information taken from the config map, or the entire config map, through various means. The config map may be passed into the test via aConfigMapFixture
, for example. ClassConfigMapWrapperSuite
represents one more way to get at the config map inside a suite of test:ConfigMapWrapperSuite
will pass the config map to the constructor of your suite class, bringing it easily into scope for tests and helper methods alike.Having the config map passed to the suite constructor might be more convenient in some cases, but in the case of the
org.scalatest.path
traits, it is necessary if a test needs information from a config map. The reason is that in a path trait, the test code is executed eagerly, beforerun
is invoked. The results of the tests are registered when the tests are executed, and those results are merely reported oncerun
is invoked. Thus by the timerun
has been invoked, it is too late to get the config map to the tests, which have already been executed. Using aConfigMapWrapperSuite
solves that problem. By passing the config map to the constructor, it is available early enough for the running tests to use it. Here's an example:import org.scalatest._ @WrapWith(classOf[ConfigMapWrapperSuite]) class ExampleSpec(configMap: ConfigMap) extends path.FunSpec { describe("A widget database") { it("should contain consistent values") { val dbName = configMap("WidgetDbName") // Can access config map // ... } } }
-
trait
DiagrammedAssertions
extends Assertions
Sub-trait of
Assertions
that overrideassert
andassume
methods to include a diagram showing the values of expression in the error message when the assertion or assumption fails.Sub-trait of
Assertions
that overrideassert
andassume
methods to include a diagram showing the values of expression in the error message when the assertion or assumption fails.Here are some examples:
scala> import DiagrammedAssertions._ import DiagrammedAssertions._ scala> assert(a == b || c >= d) org.scalatest.exceptions.TestFailedException: assert(a == b || c >= d) | | | | | | | 1 | 2 | 3 | 4 | | false | false false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(xs.exists(_ == 4)) org.scalatest.exceptions.TestFailedException: assert(xs.exists(_ == 4)) | | | false List(1, 2, 3) at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert("hello".startsWith("h") && "goodbye".endsWith("y")) org.scalatest.exceptions.TestFailedException: assert("hello".startsWith("h") && "goodbye".endsWith("y")) | | | | | | | "hello" true "h" | "goodbye" false "y" false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(num.isInstanceOf[Int]) org.scalatest.exceptions.TestFailedException: assert(num.isInstanceOf[Int]) | | 1.0 false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(Some(2).isEmpty) org.scalatest.exceptions.TestFailedException: assert(Some(2).isEmpty) | | | | 2 false Some(2) at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(None.isDefined) org.scalatest.exceptions.TestFailedException: assert(None.isDefined) | | None false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(xs.exists(i => i > 10)) org.scalatest.exceptions.TestFailedException: assert(xs.exists(i => i > 10)) | | | false List(1, 2, 3) at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
If the expression passed to
assert
orassume
spans more than one line,DiagrammedAssertions
falls back to the default style of error message, since drawing a diagram would be difficult. Here's an example showing howDiagrammedAssertions
will treat a multi-line assertion (i.e., you don't get a diagram):scala> assert("hello".startsWith("h") && | "goodbye".endsWith("y")) org.scalatest.exceptions.TestFailedException: "hello" started with "h", but "goodbye" did not end with "y" at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
Also, since an expression diagram essentially represents multi-line ascii art, if a clue string is provided, it appears above the diagram, not after it. It will often also show up in the diagram:
scala> assert(None.isDefined, "Don't do this at home") org.scalatest.exceptions.TestFailedException: Don't do this at home assert(None.isDefined, "Don't do this at home") | | None false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(None.isDefined, | "Don't do this at home") org.scalatest.exceptions.TestFailedException: Don't do this at home assert(None.isDefined, | | None false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
Trait
DiagrammedAssertions
was inspired by Peter Niederwieser's work in Spock and Expecty. -
trait
DiagrammedExpr
[T] extends AnyRef
A trait that represent an expression recorded by
DiagrammedExprMacro
, which includes the following members:A trait that represent an expression recorded by
DiagrammedExprMacro
, which includes the following members:- a boolean value
- an anchor that records the position of this expression
- anchor values of this expression (including sub-expressions)
DiagrammedExpr
is used by code generated fromDiagrammedAssertionsMacro
, it needs to be public so that the generated code can be compiled. It is expected that ScalaTest users would ever need to useDiagrammedExpr
directly. -
trait
DistributedSuiteSorter
extends AnyRef
A sorter for the events of a run's distributed suites.
A sorter for the events of a run's distributed suites.
This trait is used, for example, when
-PS
is passed toRunner
, to sort the events of distributed suites such that each suite's events are propagated together, with a timeout if an event takes too long. -
trait
DistributedTestSorter
extends AnyRef
A sorter for the events of a suite's distributed tests.
A sorter for the events of a suite's distributed tests.
This trait is used, for example, by
ParallelTestExecution
to sort the events of tests back into sequential order, with a timeout if an event takes too long. -
trait
Distributor
extends AnyRef
Trait whose instances facilitate parallel execution of
Suite
s.Trait whose instances facilitate parallel execution of
Suite
s. An optionalDistributor
is passed to therun
method ofSuite
. If aDistributor
is indeed passed, traitSuite
's implementation ofrun
will populate thatDistributor
with its nestedSuite
s (by passing them to theDistributor
'sapply
method) rather than executing the nestedSuite
s directly. It is then up to another thread or process to execute thoseSuite
s.If you have a set of nested
Suite
s that must be executed sequentially, you can mix in traitSequentialNestedSuiteExecution
, which overridesrunNestedSuites
and callssuper
'srunNestedSuites
implementation, passing inNone
for theDistributor
.Implementations of this trait must be thread safe.
-
trait
DoNotDiscover
extends Annotation
Annotation used to indicate that an otherwise discoverable test class should not be discovered.
Annotation used to indicate that an otherwise discoverable test class should not be discovered.
Note: This is actually an annotation defined in Java, not a Scala trait. It must be defined in Java instead of Scala so it will be accessible at runtime. It has been inserted into Scaladoc by pretending it is a trait.
ScalaTest will discover any class that either extends
Suite
and has a public, no-arg constructor, or is annotated with a validWrapWith
annotation. If you wish to prevent a class from being discovered, simply annotate it withDoNotDiscover
, like this:import org.scalatest._ @DoNotDiscover class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { intercept[NoSuchElementException] { Set.empty.head } } }
ScalaTest will run classes annotated with
DoNotDiscover
if asked to explicitly, it just won't discover them.Note that because reflection is not supported on Scala.js, this annotation will only work on the JVM, not on Scala.js.
-
trait
Documenter
extends AnyRef
Trait to which markup text tests can be reported.
Trait to which markup text tests can be reported.
Note:
Documenter
will be described in more detail in a future 2.0 milestone release. As of this release you can't see its effects yet. -
trait
Documenting
extends AnyRef
Trait that contains a
markup
method, which can be used to send markup to theReporter
.Trait that contains a
markup
method, which can be used to send markup to theReporter
. -
final
case class
DynaTags
(suiteTags: Map[String, Set[String]], testTags: Map[String, Map[String, Set[String]]]) extends Serializable with Product
Dynamic tags for a run.
Dynamic tags for a run.
Instances of this class are passed to the
Filter
constructor to support running selected suites and tests via dynamic tagging. For example, dynamic tags can be used to rerun tests that failed previously, or tests selected via a wildcard fromRunner
or the Scala interpreter.- suiteTags
a map from String suite ID to a set of tags for that suite.
- testTags
a map from String suite ID to a map, whose keys are test names and values the tags for that test.
- Exceptions thrown
NullPointerException
if eithersuiteTags
ortestTags
isnull
-
trait
EitherValues
extends AnyRef
Trait that provides an implicit conversion that adds
left.value
andright.value
methods toEither
, which will return the selected value of theEither
if defined, or throwTestFailedException
if not.Trait that provides an implicit conversion that adds
left.value
andright.value
methods toEither
, which will return the selected value of theEither
if defined, or throwTestFailedException
if not.This construct allows you to express in one statement that an
Either
should be left or right and that its value should meet some expectation. Here's are some examples:either1.right.value should be > 9 either2.left.value should be ("Muchas problemas")
Or, using assertions instead of matcher expressions:
assert(either1.right.value > 9) assert(either2.left.value === "Muchas problemas")
Were you to simply invoke
right.get
orleft.get
on theEither
, if theEither
wasn't defined as expected (e.g., it was aLeft
when you expected aRight
), it would throw aNoSuchElementException
:val either: Either[String, Int] = Left("Muchas problemas") either.right.get should be > 9 // either.right.get throws NoSuchElementException
The
NoSuchElementException
would cause the test to fail, but without providing a stack depth pointing to the failing line of test code. This stack depth, provided byTestFailedException
(and a few other ScalaTest exceptions), makes it quicker for users to navigate to the cause of the failure. WithoutEitherValues
, to get a stack depth exception you would need to make two statements, like this:val either: Either[String, Int] = Left("Muchas problemas") either should be ('right) // throws TestFailedException either.right.get should be > 9
The
EitherValues
trait allows you to state that more concisely:val either: Either[String, Int] = Left("Muchas problemas") either.right.value should be > 9 // either.right.value throws TestFailedException
-
case class
Entry
[K, V](key: K, value: V) extends java.util.Map.Entry[K, V] with Product with Serializable
A case class implementation of
java.util.Map.Entry
to make it easier to test JavaMap
s with ScalaTest Matchers.A case class implementation of
java.util.Map.Entry
to make it easier to test JavaMap
s with ScalaTest Matchers.In Java,
java.util.Map
is not a subtype ofjava.util.Collection
, and does not actually define an element type. You can ask a JavaMap
for an “entry set” via theentrySet
method, which will return theMap
's key/value pairs wrapped in a set ofjava.util.Map.Entry
, but aMap
is not actually a collection ofEntry
. To make JavaMap
s easier to work with, however, ScalaTest matchers allows you to treat a JavaMap
as a collection ofEntry
, and defines this convenience implementation ofjava.util.Map.Entry
. Here's how you use it:javaMap should contain (Entry(2, 3)) javaMap should contain oneOf (Entry(2, 3), Entry(3, 4))
- key
the key of this entry
- value
the value of this entry
-
sealed abstract
class
Exceptional
extends Outcome
Superclass for the two outcomes of running a test that contain an exception:
Failed
andCanceled
.Superclass for the two outcomes of running a test that contain an exception:
Failed
andCanceled
.This class provides a
toOption
method that returns aSome
wrapping the contained exception, and anisExceptional
field with the valuetrue
. It's companion object provides an extractor that enables patterns that match a test that either failed or canceled, as in:outcome match { case Exceptional(ex) => // handle failed or canceled case case _ => // handle succeeded, pending, or omitted case }
-
case class
Failed
(exception: Throwable) extends Exceptional with Product with Serializable
Outcome for a test that failed, containing an exception describing the cause of the failure.
Outcome for a test that failed, containing an exception describing the cause of the failure.
Note: the difference between this
Failed
class and the similarly namedFailedStatus
object is that an instance of this class indicates one test failed, whereas theFailedStatus
object indicates either one or more tests failed and/or one or more suites aborted during a run. Both are used as the result type ofSuite
lifecycle methods, butFailed
is a possible result ofwithFixture
, whereasFailedStatus
is a possible result ofrun
,runNestedSuites
,runTests
, orrunTest
. In short,Failed
is always just about one test, whereasFailedStatus
could be about something larger: multiple tests or an entire suite. -
class
FeatureSpec
extends FeatureSpecLike
A suite of tests in which each test represents one scenario of a feature.
A suite of tests in which each test represents one scenario of a feature.
FeatureSpec
is intended for writing tests that are "higher level" than unit tests, for example, integration tests, functional tests, and acceptance tests. You can useFeatureSpec
for unit testing if you prefer, however.Recommended Usage: Class FeatureSpec
is primarily intended for acceptance testing, including facilitating the process of programmers working alongside non-programmers to define the acceptance requirements.Although not required,
FeatureSpec
is often used together withGivenWhenThen
to express acceptance requirements in more detail. Here's an example:package org.scalatest.examples.featurespec import org.scalatest._ class TVSet { private var on: Boolean = false def isOn: Boolean = on def pressPowerButton() { on = !on } } class TVSetSpec extends FeatureSpec with GivenWhenThen { info("As a TV set owner") info("I want to be able to turn the TV on and off") info("So I can watch TV when I want") info("And save energy when I'm not watching TV") feature("TV power button") { scenario("User presses power button when TV is off") { Given("a TV set that is switched off") val tv = new TVSet assert(!tv.isOn) When("the power button is pressed") tv.pressPowerButton() Then("the TV should switch on") assert(tv.isOn) } scenario("User presses power button when TV is on") { Given("a TV set that is switched on") val tv = new TVSet tv.pressPowerButton() assert(tv.isOn) When("the power button is pressed") tv.pressPowerButton() Then("the TV should switch off") assert(!tv.isOn) } } }
Note: for more information on the calls to
Given
,When
, andThen
, see the documentation for traitGivenWhenThen
and theInformers
section below.A
FeatureSpec
contains feature clauses and scenarios. You define a feature clause withfeature
, and a scenario withscenario
. Bothfeature
andscenario
are methods, defined inFeatureSpec
, which will be invoked by the primary constructor ofTVSetSpec
. A feature clause describes a feature of the subject (class or other entity) you are specifying and testing. In the previous example, the subject under specification and test is a TV set. The feature being specified and tested is the behavior of a TV set when its power button is pressed. With each scenario you provide a string (the spec text) that specifies the behavior of the subject for one scenario in which the feature may be used, and a block of code that tests that behavior. You place the spec text between the parentheses, followed by the test code between curly braces. The test code will be wrapped up as a function passed as a by-name parameter toscenario
, which will register the test for later execution.A
FeatureSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Scenarios can only be registered with the
scenario
method while theFeatureSpec
is in its registration phase. Any attempt to register a scenario after theFeatureSpec
has entered its ready phase, i.e., afterrun
has been invoked on theFeatureSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingFeatureSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Each scenario represents one test. The name of the test is the spec text passed to the
scenario
method. The feature name does not appear as part of the test name. In aFeatureSpec
, therefore, you must take care to ensure that each test has a unique name (in other words, that eachscenario
has unique spec text).When you run a
FeatureSpec
, it will sendFormatter
s in the events it sends to theReporter
. ScalaTest's built-in reporters will report these events in such a way that the output is easy to read as an informal specification of the subject being tested. For example, were you to runTVSetSpec
from within the Scala interpreter:scala> org.scalatest.run(new TVSetSpec)
You would see:
TVSetSpec: As a TV set owner I want to be able to turn the TV on and off So I can watch TV when I want And save energy when I'm not watching TV Feature: TV power button Scenario: User presses power button when TV is off Given a TV set that is switched off When the power button is pressed Then the TV should switch on Scenario: User presses power button when TV is on Given a TV set that is switched on When the power button is pressed Then the TV should switch off
Or, to run just the “
Feature: TV power button Scenario: User presses power button when TV is on
” method, you could pass that test's name, or any unique substring of the name, such as"TV is on"
. Here's an example:scala> org.scalatest.run(new TVSetSpec, "TV is on") TVSetSpec: As a TV set owner I want to be able to turn the TV on and off So I can watch TV when I want And save energy when I'm not watching TV Feature: TV power button Scenario: User presses power button when TV is on Given a TV set that is switched on When the power button is pressed Then the TV should switch off
Note: Trait
FeatureSpec
's syntax is in part inspired by Cucumber, a Ruby BDD framework.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,
FeatureSpec
provides registration methods that start withignore
instead ofscenario
. For example, to temporarily disable the test namedaddition
, just change “scenario
” into “ignore
,” like this:package org.scalatest.examples.featurespec.ignore import org.scalatest.FeatureSpec class TVSet { private var on: Boolean = false def isOn: Boolean = on def pressPowerButton() { on = !on } } class TVSetSpec extends FeatureSpec { feature("TV power button") { ignore("User presses power button when TV is off") { val tv = new TVSet assert(!tv.isOn) tv.pressPowerButton() assert(tv.isOn) } scenario("User presses power button when TV is on") { val tv = new TVSet tv.pressPowerButton() assert(tv.isOn) tv.pressPowerButton() assert(!tv.isOn) } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new TVSetSpec)
It will run only the second scenario and report that the first scenario was ignored:
TVSetSpec: Feature: TV power button Scenario: User presses power button when TV is off !!! IGNORED !!! Scenario: User presses power button when TV is on
Informers
One of the parameters to
FeatureSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the default reporting done byFeatureSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a scenario to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byFeatureSpec
to pass such information to the reporter. You can see this in action in the initial example of this trait's documentation.Documenters
FeatureSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FlatSpec
that usesmarkup
:package org.scalatest.examples.featurespec.markup import collection.mutable import org.scalatest._ class SetSpec extends FeatureSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } feature("An element can be added to an empty mutable Set") { scenario("When an element is added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.featurespec.note import collection.mutable import org.scalatest._ class SetSpec extends FeatureSpec { feature("An element can be added to an empty mutable Set") { scenario("When an element is added to an empty mutable Set") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: Feature: An element can be added to an empty mutable Set + notes are sent immediately + alerts are also sent immediately Scenario: When an element is added to an empty mutable Set info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented. You can mark tests as pending in aFeatureSpec
like this:package org.scalatest.examples.featurespec.pending import org.scalatest.FeatureSpec class TVSet { private var on: Boolean = false def isOn: Boolean = on def pressPowerButton() { on = !on } } class TVSetSpec extends FeatureSpec { feature("TV power button") { scenario("User presses power button when TV is off") (pending) scenario("User presses power button when TV is on") { val tv = new TVSet tv.pressPowerButton() assert(tv.isOn) tv.pressPowerButton() assert(!tv.isOn) } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofTVSetSpec
with:scala> org.scalatest.run(new TVSetSpec)
It will run both tests, but report that
When empty should have size 0
is pending. You'll see:TVSetSpec: Feature: TV power button Scenario: User presses power button when TV is off (pending) Scenario: User presses power button when TV is on
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait. For example, the following snippet in aFeatureSpec
:package org.scalatest.examples.featurespec.infopending import org.scalatest._ class TVSet { private var on: Boolean = false def isOn: Boolean = on def pressPowerButton() { on = !on } } class TVSetSpec extends FeatureSpec with GivenWhenThen { info("As a TV set owner") info("I want to be able to turn the TV on and off") info("So I can watch TV when I want") info("And save energy when I'm not watching TV") feature("TV power button") { scenario("User presses power button when TV is off") { Given("a TV that is switched off") When("the power button is pressed") Then("the TV should switch on") pending } scenario("User presses power button when TV is on") { Given("a TV that is switched on") When("the power button is pressed") Then("the TV should switch off") pending } } }
Would yield the following output when run in the interpreter:
scala> org.scalatest.run(new TVSetSpec) TVSetSpec: As a TV set owner I want to be able to turn the TV on and off So I can watch TV when I want And save energy when I'm not watching TV Feature: TV power button Scenario: User presses power button when TV is off (pending) Given a TV that is switched off When the power button is pressed Then the TV should switch on Scenario: User presses power button when TV is on (pending) Given a TV that is switched on When the power button is pressed Then the TV should switch off
Tagging tests
A
FeatureSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aFeatureSpec
, groups of tests can optionally be included and/or excluded. To tag aFeatureSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forFeatureSpec
s like this:package org.scalatest.examples.featurespec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
FeatureSpec
tests into groups with tags like this:import org.scalatest.FeatureSpec import org.scalatest.tagobjects.Slow class TVSet { private var on: Boolean = false def isOn: Boolean = on def pressPowerButton() { on = !on } } class TVSetSpec extends FeatureSpec { feature("TV power button") { scenario("User presses power button when TV is off", Slow) { val tv = new TVSet assert(!tv.isOn) tv.pressPowerButton() assert(tv.isOn) } scenario("User presses power button when TV is on", Slow, DbTest) { val tv = new TVSet tv.pressPowerButton() assert(tv.isOn) tv.pressPowerButton() assert(!tv.isOn) } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aFeatureSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.featurespec.getfixture import org.scalatest.FeatureSpec import collection.mutable.ListBuffer class ExampleSpec extends FeatureSpec { class Fixture { val builder = new StringBuilder("ScalaTest is designed to ") val buffer = new ListBuffer[String] } def fixture = new Fixture feature("Simplicity") { scenario("User needs to read test code written by others") { val f = fixture f.builder.append("encourage clear code!") assert(f.builder.toString === "ScalaTest is designed to encourage clear code!") assert(f.buffer.isEmpty) f.buffer += "sweet" } scenario("User needs to understand what the tests are doing") { val f = fixture f.builder.append("be easy to reason about!") assert(f.builder.toString === "ScalaTest is designed to be easy to reason about!") assert(f.buffer.isEmpty) } } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.featurespec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FeatureSpec class ExampleSpec extends FeatureSpec { trait Builder { val builder = new StringBuilder("ScalaTest is designed to ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is", "designed", "to") } feature("Simplicity") { // This test needs the StringBuilder fixture scenario("User needs to read test code written by others") { new Builder { builder.append("encourage clear code!") assert(builder.toString === "ScalaTest is designed to encourage clear code!") } } // This test needs the ListBuffer[String] fixture scenario("User needs to understand what the tests are doing") { new Buffer { buffer += ("be", "easy", "to", "reason", "about!") assert(buffer === List("ScalaTest", "is", "designed", "to", "be", "easy", "to", "reason", "about!")) } } // This test needs both the StringBuilder and ListBuffer scenario("User needs to write tests") { new Builder with Buffer { builder.append("be easy to learn!") buffer += ("be", "easy", "to", "remember", "how", "to", "write!") assert(builder.toString === "ScalaTest is designed to be easy to learn!") assert(buffer === List("ScalaTest", "is", "designed", "to", "be", "easy", "to", "remember", "how", "to", "write!")) } } } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. That is to say, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.featurespec.noargtest import java.io.File import org.scalatest._ class ExampleSpec extends FeatureSpec { override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } scenario("This scenario should succeed") { assert(1 + 1 === 2) } scenario("This scenario should fail") { assert(1 + 1 === 3) } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSpec) ExampleSpec: Scenario: This scenario should succeed Scenario: This scenario should fail *** FAILED *** 2 did not equal 3 (
:115) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.featurespec.loanfixture import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.FeatureSpec import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSpec extends FeatureSpec { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is designed to ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is designed to ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } feature("Simplicity") { // This test needs the file fixture scenario("User needs to read test code written by others") { withFile { (file, writer) => writer.write("encourage clear code!") writer.flush() assert(file.length === 46) } } // This test needs the database fixture scenario("User needs to understand what the tests are doing") { withDatabase { db => db.append("be easy to reason about!") assert(db.toString === "ScalaTest is designed to be easy to reason about!") } } // This test needs both the file and the database scenario("User needs to write tests") { withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("be easy to learn!") writer.write("be easy to remember how to write!") writer.flush() assert(db.toString === "ScalaTest is designed to be easy to learn!") assert(file.length === 58) } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
==== Overriding
withFixture(OneArgTest)
==== If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using afixture.FeatureSpec
and overridingwithFixture(OneArgTest)
. Each test in afixture.FeatureSpec
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function. To enable the stacking of traits that definewithFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:package org.scalatest.examples.featurespec.oneargtest import org.scalatest.fixture import java.io._ class ExampleSpec extends fixture.FeatureSpec { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { // create the fixture val file = File.createTempFile("hello", "world") val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is designed to be ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } feature("Simplicity") { scenario("User needs to read test code written by others") { f => f.writer.write("encourage clear code!") f.writer.flush() assert(f.file.length === 49) } scenario("User needs to understand what the tests are doing") { f => f.writer.write("be easy to reason about!") f.writer.flush() assert(f.file.length === 52) } } }
In this example, the tests actually required two fixture objects, aFile
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.FeatureSpec
. ==== Mixing inBeforeAndAfter
==== In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in traitBeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.featurespec.beforeandafter import org.scalatest._ import collection.mutable.ListBuffer class ExampleSpec extends FeatureSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is designed to ") } after { builder.clear() buffer.clear() } feature("Simplicity") { scenario("User needs to read test code written by others") { builder.append("encourage clear code!") assert(builder.toString === "ScalaTest is designed to encourage clear code!") assert(buffer.isEmpty) buffer += "sweet" } scenario("User needs to understand what the tests are doing") { builder.append("be easy to reason about!") assert(builder.toString === "ScalaTest is designed to be easy to reason about!") assert(buffer.isEmpty) } } }
Note that the only waybefore
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects. AlthoughBeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits. == Composing fixtures by stacking traits == In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placingwithFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.featurespec.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is designed to ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSpec extends FeatureSpec with Builder with Buffer { feature("Simplicity") { scenario("User needs to read test code written by others") { builder.append("encourage clear code!") assert(builder.toString === "ScalaTest is designed to encourage clear code!") assert(buffer.isEmpty) buffer += "clear" } scenario("User needs to understand what the tests are doing") { builder.append("be easy to reason about!") assert(builder.toString === "ScalaTest is designed to be easy to reason about!") assert(buffer.isEmpty) buffer += "easy" } } }
By mixing in both theBuilder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends FeatureSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:class Example3Spec extends FeatureSpec with Builder
Another way to create stackable fixture traits is by extending theBeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.featurespec.composingbeforeandaftereach import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is designed to ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally buffer.clear() } } class ExampleSpec extends FeatureSpec with Builder with Buffer { feature("Simplicity") { scenario("User needs to read test code written by others") { builder.append("encourage clear code!") assert(builder.toString === "ScalaTest is designed to encourage clear code!") assert(buffer.isEmpty) buffer += "clear" } scenario("User needs to understand what the tests are doing") { builder.append("be easy to reason about!") assert(builder.toString === "ScalaTest is designed to be easy to reason about!") assert(buffer.isEmpty) buffer += "easy" } } }
To get the same ordering aswithFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception. The difference between stacking traits that extendBeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event. == Shared scenarios == Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in aFeatureSpec
, you first place shared tests (i.e., shared scenarios) in behavior functions. These behavior functions will be invoked during the construction phase of anyFeatureSpec
that uses them, so that the scenarios they contain will be registered as scenarios in thatFeatureSpec
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test theStack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several scenarios that make sense any time the stack is non-empty. Thus you'd ideally want to run those same scenarios for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these scenarios out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourFeatureSpec
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared scenarios are run for all three fixtures. You can define a behavior function that encapsulates these shared scenarios inside theFeatureSpec
that uses them. If they are shared between differentFeatureSpec
s, however, you could also define them in a separate trait that is mixed into eachFeatureSpec
that uses them. For example, here thenonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared scenarios for non-full stacks:import org.scalatest.FeatureSpec import org.scalatest.GivenWhenThen import org.scalatestexamples.helpers.Stack trait FeatureSpecStackBehaviors { this: FeatureSpec with GivenWhenThen => def nonEmptyStack(createNonEmptyStack: => Stack[Int], lastItemAdded: Int) { scenario("empty is invoked on this non-empty stack: " + createNonEmptyStack.toString) { Given("a non-empty stack") val stack = createNonEmptyStack When("empty is invoked on the stack") Then("empty returns false") assert(!stack.empty) } scenario("peek is invoked on this non-empty stack: " + createNonEmptyStack.toString) { Given("a non-empty stack") val stack = createNonEmptyStack val size = stack.size When("peek is invoked on the stack") Then("peek returns the last item added") assert(stack.peek === lastItemAdded) And("the size of the stack is the same as before") assert(stack.size === size) } scenario("pop is invoked on this non-empty stack: " + createNonEmptyStack.toString) { Given("a non-empty stack") val stack = createNonEmptyStack val size = stack.size When("pop is invoked on the stack") Then("pop returns the last item added") assert(stack.pop === lastItemAdded) And("the size of the stack one less than before") assert(stack.size === size - 1) } } def nonFullStack(createNonFullStack: => Stack[Int]) { scenario("full is invoked on this non-full stack: " + createNonFullStack.toString) { Given("a non-full stack") val stack = createNonFullStack When("full is invoked on the stack") Then("full returns false") assert(!stack.full) } scenario("push is invoked on this non-full stack: " + createNonFullStack.toString) { Given("a non-full stack") val stack = createNonFullStack val size = stack.size When("push is invoked on the stack") stack.push(7) Then("the size of the stack is one greater than before") assert(stack.size === size + 1) And("the top of the stack contains the pushed value") assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, butFeatureSpec
offers a DSL for the purpose, which looks like this:scenariosFor(nonEmptyStack(stackWithOneItem, lastValuePushed)) scenariosFor(nonFullStack(stackWithOneItem))
If you prefer to use an imperative style to change fixtures, for example by mixing inBeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:scenariosFor(nonEmptyStack) // assuming lastValuePushed is also in scope inside nonEmptyStack scenariosFor(nonFullStack)
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:import org.scalatest.FeatureSpec import org.scalatest.GivenWhenThen import org.scalatestexamples.helpers.Stack class StackFeatureSpec extends FeatureSpec with GivenWhenThen with FeatureSpecStackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 feature("A Stack is pushed and popped") { scenario("empty is invoked on an empty stack") { Given("an empty stack") val stack = emptyStack When("empty is invoked on the stack") Then("empty returns true") assert(stack.empty) } scenario("peek is invoked on an empty stack") { Given("an empty stack") val stack = emptyStack When("peek is invoked on the stack") Then("peek throws IllegalStateException") assertThrows[IllegalStateException] { stack.peek } } scenario("pop is invoked on an empty stack") { Given("an empty stack") val stack = emptyStack When("pop is invoked on the stack") Then("pop throws IllegalStateException") assertThrows[IllegalStateException] { emptyStack.pop } } scenariosFor(nonEmptyStack(stackWithOneItem, lastValuePushed)) scenariosFor(nonFullStack(stackWithOneItem)) scenariosFor(nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed)) scenariosFor(nonFullStack(stackWithOneItemLessThanCapacity)) scenario("full is invoked on a full stack") { Given("an full stack") val stack = fullStack When("full is invoked on the stack") Then("full returns true") assert(stack.full) } scenariosFor(nonEmptyStack(fullStack, lastValuePushed)) scenario("push is invoked on a full stack") { Given("an full stack") val stack = fullStack When("push is invoked on the stack") Then("push throws IllegalStateException") assertThrows[IllegalStateException] { stack.push(10) } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:scala> (new StackFeatureSpec).execute() Feature: A Stack is pushed and popped Scenario: empty is invoked on an empty stack Given an empty stack When empty is invoked on the stack Then empty returns true Scenario: peek is invoked on an empty stack Given an empty stack When peek is invoked on the stack Then peek throws IllegalStateException Scenario: pop is invoked on an empty stack Given an empty stack When pop is invoked on the stack Then pop throws IllegalStateException Scenario: empty is invoked on this non-empty stack: Stack(9) Given a non-empty stack When empty is invoked on the stack Then empty returns false Scenario: peek is invoked on this non-empty stack: Stack(9) Given a non-empty stack When peek is invoked on the stack Then peek returns the last item added And the size of the stack is the same as before Scenario: pop is invoked on this non-empty stack: Stack(9) Given a non-empty stack When pop is invoked on the stack Then pop returns the last item added And the size of the stack one less than before Scenario: full is invoked on this non-full stack: Stack(9) Given a non-full stack When full is invoked on the stack Then full returns false Scenario: push is invoked on this non-full stack: Stack(9) Given a non-full stack When push is invoked on the stack Then the size of the stack is one greater than before And the top of the stack contains the pushed value Scenario: empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) Given a non-empty stack When empty is invoked on the stack Then empty returns false Scenario: peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) Given a non-empty stack When peek is invoked on the stack Then peek returns the last item added And the size of the stack is the same as before Scenario: pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) Given a non-empty stack When pop is invoked on the stack Then pop returns the last item added And the size of the stack one less than before Scenario: full is invoked on this non-full stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) Given a non-full stack When full is invoked on the stack Then full returns false Scenario: push is invoked on this non-full stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) Given a non-full stack When push is invoked on the stack Then the size of the stack is one greater than before And the top of the stack contains the pushed value Scenario: full is invoked on a full stack Given an full stack When full is invoked on the stack Then full returns true Scenario: empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) Given a non-empty stack When empty is invoked on the stack Then empty returns false Scenario: peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) Given a non-empty stack When peek is invoked on the stack Then peek returns the last item added And the size of the stack is the same as before Scenario: pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) Given a non-empty stack When pop is invoked on the stack Then pop returns the last item added And the size of the stack one less than before Scenario: push is invoked on a full stack Given an full stack When push is invoked on the stack Then push throws IllegalStateException
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. Although in aFeatureSpec
, thefeature
clause is a nesting construct analogous toFunSpec
'sdescribe
clause, you many sometimes need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in aFeatureSpec
, you can pass in a prefix or suffix string to add to each test name. You can pass this string the same way you pass any other data needed by the shared tests, or just calltoString
on the shared fixture object. This is the approach taken by the previousFeatureSpecStackBehaviors
example. Given thisFeatureSpecStackBehaviors
trait, calling it with thestackWithOneItem
fixture, like this:scenariosFor(nonEmptyStack(stackWithOneItem, lastValuePushed))
yields test names: -empty is invoked on this non-empty stack: Stack(9)
-peek is invoked on this non-empty stack: Stack(9)
-pop is invoked on this non-empty stack: Stack(9)
Whereas calling it with thestackWithOneItemLessThanCapacity
fixture, like this:scenariosFor(nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed))
yields different test names: -empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
-peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
-pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
-
trait
FeatureSpecLike
extends TestSuite with TestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
FeatureSpec
, which represents a suite of tests in which each test represents one scenario of a feature.Implementation trait for class
FeatureSpec
, which represents a suite of tests in which each test represents one scenario of a feature.FeatureSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofFeatureSpec
into some other class, you can use this trait instead, because classFeatureSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
FeatureSpec
. -
final
class
Filter
extends Serializable
Filter whose
apply
method determines which of the passed tests to run and ignore based on tags to include and exclude passed as as class parameters.Filter whose
apply
method determines which of the passed tests to run and ignore based on tags to include and exclude passed as as class parameters.This class handles the
org.scalatest.Ignore
tag specially, in that itsapply
method indicates which tests should be ignored based on whether they are tagged withorg.scalatest.Ignore
. If"org.scalatest.Ignore"
is not passed in thetagsToExclude
set, it will be implicitly added. However, if thetagsToInclude
option is defined, and the contained set does not include"org.scalatest.Ignore"
, then only those tests that are both tagged withorg.scalatest.Ignore
and at least one of the tags in thetagsToInclude
set will be included in the result ofapply
and marked as ignored (so long as the test is not also marked with a tag other thanorg.scalatest.Ignore
that is a member of thetagsToExclude
set. For example, ifSlowAsMolasses
is a member of thetagsToInclude
set and a test is tagged with bothorg.scalatest.Ignore
andSlowAsMolasses
, andSlowAsMolasses
appears in thetagsToExclude
set, theSlowAsMolasses
tag will "overpower" theorg.scalatest.Ignore
tag, and the test will be filtered out entirely rather than being ignored.- Exceptions thrown
IllegalArgumentException
iftagsToInclude
is defined, but contains an empty setNullArgumentException
if eithertagsToInclude
ortagsToExclude
are null
-
trait
Finders
extends Annotation
Annotation used to mark a trait or class as defining a testing style that has a
org.scalatest.finders.Finder
implementation, which IDEs and other tools can use to discover tests and scopes.Annotation used to mark a trait or class as defining a testing style that has a
org.scalatest.finders.Finder
implementation, which IDEs and other tools can use to discover tests and scopes.Note: This is actually an annotation defined in Java, not a Scala trait. It must be defined in Java instead of Scala so it will be accessible at runtime. It has been inserted into Scaladoc by pretending it is a trait.
This annotation is used to enable different styles of testing, including both native ScalaTest styles and custom user-created styles, to have rich IDE support. The "Finder API" is released separately from ScalaTest proper, because it is only used by tools such as IDEs.
-
trait
FixtureContext
extends scalatest.compatible.Assertion
Marker trait for fixture-context objects, that enables them to be used in testing styles that require type
Assertion
Marker trait for fixture-context objects, that enables them to be used in testing styles that require type
Assertion
A fixture-context object is a way to share fixtures between different tests that is most useful when different tests need different combinations of fixture objects. The fixture-context object technique is only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example
FlatSpec
in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:package org.scalatest.examples.flatspec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FlatSpec import org.scalatest.FixtureContext class ExampleSpec extends FlatSpec { trait Builder extends FixtureContext { val builder = new StringBuilder("ScalaTest is ") } trait Buffer extends FixtureContext { val buffer = ListBuffer("ScalaTest", "is") } // This test needs the StringBuilder fixture "Testing" should "be productive" in new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } // This test needs the ListBuffer[String] fixture "Test code" should "be readable" in new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } // This test needs both the StringBuilder and ListBuffer it should "be clear and concise" in new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } }
Extending
FixtureContext
, which extends traitorg.scalatest.compatible.Assertion
makes it more convenient to use fixture-context objects in styles, such as async styles, that require test bodies to have typeAssertion
. -
class
FlatSpec
extends FlatSpecLike
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Recommended Usage: Class FlatSpec
is a good first step for teams wishing to move from xUnit to BDD, because its structure is flat like xUnit, so simple and familiar, but the test names must be written in a specification style: “X should Y,” “A must B,” etc.Trait
FlatSpec
is so named because your specification text and tests line up flat against the left-side indentation level, with no nesting needed. Here's an exampleFlatSpec
:package org.scalatest.examples.flatspec import org.scalatest.FlatSpec class SetSpec extends FlatSpec { behavior of "An empty Set" it should "have size 0" in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
Note: you can use
must
orcan
as well asshould
in aFlatSpec
. For example, instead ofit should "have
..., you could writeit must "have
... orit can "have
....Instead of using a
behavior of
clause, you can alternatively use a shorthand syntax in which you replace the firstit
with the subject string, like this:package org.scalatest.examples.flatspec import org.scalatest.FlatSpec class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
Running either of the two previous versions of
SetSpec
in the Scala interpreter would yield:An empty Set - should have size 0 - should produce NoSuchElementException when head is invoked
In a
FlatSpec
you write a one (or more) sentence specification for each bit of behavior you wish to specify and test. Each specification sentence has a "subject," which is sometimes called the system under test (or SUT). The subject is the entity being specified and tested and also serves as the subject of the sentences you write for each test. Often you will want to write multiple tests for the same subject. In aFlatSpec
, you name the subject once, with abehavior of
clause or its shorthand, then write tests for that subject withit should
/must
/can "do something"
phrases. Eachit
refers to the most recently declared subject. For example, the four tests shown in this snippet are all testing a stack that contains one item:behavior of "A Stack (with one item)" it should "be non-empty" in {} it should "return the top item on peek" in {} it should "not remove the top item on peek" in {} it should "remove the top item on pop" in {}
The same is true if the tests are written using the shorthand notation:
"A Stack (with one item)" should "be non-empty" in {} it should "return the top item on peek" in {} it should "not remove the top item on peek" in {} it should "remove the top item on pop" in {}
In a
FlatSpec
, therefore, to figure out what "it
" means, you just scan vertically until you find the most recent use ofbehavior of
or the shorthand notation.Because sometimes the subject could be plural, you can alternatively use
they
instead ofit
:"The combinators" should "be easy to learn" in {} they should "be efficient" in {} they should "do something cool" in {}
A
FlatSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered while the
FlatSpec
is in its registration phase. Any attempt to register a test after theFlatSpec
has entered its ready phase, i.e., afterrun
has been invoked on theFlatSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingFlatSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.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,
FlatSpec
provides a methodignore
that can be used instead ofit
orthey
to register a test. For example, to temporarily disable the test with the name"An empty Set should produce NoSuchElementException when head is invoked"
, just change “it
” into “ignore
,” like this:package org.scalatest.examples.flatspec.ignore import org.scalatest.FlatSpec class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in { assert(Set.empty.size === 0) } ignore should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run only the first test and report that the second test was ignored:
An empty Set - should have size 0 - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
When using shorthand notation, you won't have an
it
to change intoignore
for the first test of each new subject. To ignore such tests, you must instead changein
toignore
. For example, to temporarily disable the test with the name"An empty Set should have size 0"
, change “in
” into “ignore
” like this:package org.scalatest.examples.flatspec.ignoreafter import org.scalatest.FlatSpec class SetSpec extends FlatSpec { "An empty Set" should "have size 0" ignore { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
If you run this version of
StackSpec
with:scala> org.scalatest.run(new SetSpec)
It will run only the second test and report that the first test was ignored:
An empty Set - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.flatspec.ignoreall import org.scalatest._ @Ignore class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSpec
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSpec) SetSpec: An empty Set - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.Informers
One of the parameters to
FlatSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byFlatSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byFlatSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.flatspec.info import collection.mutable import org.scalatest._ class SetSpec extends FlatSpec with GivenWhenThen { "A mutable Set" should "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } }
If you run this
FlatSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
FlatSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FlatSpec
that usesmarkup
:package org.scalatest.examples.flatspec.markup import collection.mutable import org.scalatest._ class SetSpec extends FlatSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" should "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.flatspec.note import collection.mutable import org.scalatest._ class SetSpec extends FlatSpec { "A mutable Set" should "allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, 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. You can mark tests as pending inFlatSpec
like this:package org.scalatest.examples.flatspec.pending import org.scalatest._ class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in (pending) it should "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } }
If you run this version of
FlatSpec
with:scala> org.scalatest.run(new SetSpec)
It will run both tests but report that
An empty Set should have size 0
is pending. You'll see:An empty Set - should have size 0 (pending) - should produce NoSuchElementException when head is invoked
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait. For example, the following snippet in aFlatSpec
:"The Scala language" must "add correctly" in { Given("two integers") When("they are added") Then("the result is the sum of the two numbers") pending } // ...
Would yield the following output when run in the interpreter:
The Scala language - must add correctly (pending) + Given two integers + When they are added + Then the result is the sum of the two numbers
Tagging tests
A
FlatSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aFlatSpec
, groups of tests can optionally be included and/or excluded. To tag aFlatSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forFlatSpec
s like this:package org.scalatest.examples.flatspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
FlatSpec
tests into groups with tags like this:import org.scalatest.FlatSpec import org.scalatest.tagobjects.Slow class SetSpec extends FlatSpec { behavior of "An empty Set" it should "have size 0" taggedAs(Slow) in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" taggedAs(Slow, DbTest) in { assertThrows[NoSuchElementException] { Set.empty.head } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aFlatSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or a holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.flatspec.getfixture import org.scalatest.FlatSpec import collection.mutable.ListBuffer class ExampleSpec extends FlatSpec { class Fixture { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] } def fixture = new Fixture "Testing" should "be easy" in { val f = fixture f.builder.append("easy!") assert(f.builder.toString === "ScalaTest is easy!") assert(f.buffer.isEmpty) f.buffer += "sweet" } it should "be fun" in { val f = fixture f.builder.append("fun!") assert(f.builder.toString === "ScalaTest is fun!") assert(f.buffer.isEmpty) } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, if you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.flatspec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FlatSpec class ExampleSpec extends FlatSpec { trait Builder { val builder = new StringBuilder("ScalaTest is ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is") } // This test needs the StringBuilder fixture "Testing" should "be productive" in new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } // This test needs the ListBuffer[String] fixture "Test code" should "be readable" in new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } // This test needs both the StringBuilder and ListBuffer it should "be clear and concise" in new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. That is to say, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.flatspec.noargtest import java.io.File import org.scalatest._ class ExampleSpec extends FlatSpec { override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } "This test" should "succeed" in { assert(1 + 1 === 2) } it should "fail" in { assert(1 + 1 === 3) } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includesTestData
such as the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.flatspec.loanfixture import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.FlatSpec import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSpec extends FlatSpec { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } // This test needs the file fixture "Testing" should "be productive" in withFile { (file, writer) => writer.write("productive!") writer.flush() assert(file.length === 24) } // This test needs the database fixture "Test code" should "be readable" in withDatabase { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } // This test needs both the file and the database it should "be clear and concise" in withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("clear!") writer.write("concise!") writer.flush() assert(db.toString === "ScalaTest is clear!") assert(file.length === 21) } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.FlatSpec
and overridingwithFixture(OneArgTest)
. Each test in afixture.FlatSpec
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function.To enable the stacking of traits that define
withFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:
package org.scalatest.examples.flatspec.oneargtest import org.scalatest.fixture import java.io._ class ExampleSpec extends fixture.FlatSpec { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } "Testing" should "be easy" in { f => f.writer.write("easy!") f.writer.flush() assert(f.file.length === 18) } it should "be fun" in { f => f.writer.write("fun!") f.writer.flush() assert(f.file.length === 17) } }
In this example, the tests actually required two fixture objects, a
File
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.FlatSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.flatspec.beforeandafter import org.scalatest._ import collection.mutable.ListBuffer class ExampleSpec extends FlatSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } "Testing" should "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.flatspec.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSpec extends FlatSpec with Builder with Buffer { "Testing" should "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } }
By mixing in both the
Builder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends FlatSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Spec extends FlatSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.flatspec.composingbeforeandaftereach import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally buffer.clear() } } class ExampleSpec extends FlatSpec with Builder with Buffer { "Testing" should "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it should "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in a
FlatSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyFlatSpec
that uses them, so that the tests they contain will be registered as tests in thatFlatSpec
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test the
Stack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourFlatSpec
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures. You can define a behavior function that encapsulates these shared tests inside theFlatSpec
that uses them. If they are shared between differentFlatSpec
s, however, you could also define them in a separate trait that is mixed into eachFlatSpec
that uses them.For example, here the
nonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:trait StackBehaviors { this: FlatSpec => def nonEmptyStack(newStack: => Stack[Int], lastItemAdded: Int) { it should "be non-empty" in { assert(!newStack.empty) } it should "return the top item on peek" in { assert(newStack.peek === lastItemAdded) } it should "not remove the top item on peek" in { val stack = newStack val size = stack.size assert(stack.peek === lastItemAdded) assert(stack.size === size) } it should "remove the top item on pop" in { val stack = newStack val size = stack.size assert(stack.pop === lastItemAdded) assert(stack.size === size - 1) } } def nonFullStack(newStack: => Stack[Int]) { it should "not be full" in { assert(!newStack.full) } it should "add to the top on push" in { val stack = newStack val size = stack.size stack.push(7) assert(stack.size === size + 1) assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, but
FlatSpec
offers a DSL for the purpose, which looks like this:it should behave like nonEmptyStack(stackWithOneItem, lastValuePushed) it should behave like nonFullStack(stackWithOneItem)
If you prefer to use an imperative style to change fixtures, for example by mixing in
BeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:it should behave like nonEmptyStack // assuming lastValuePushed is also in scope inside nonEmptyStack it should behave like nonFullStack
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:
class SharedTestExampleSpec extends FlatSpec with StackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 "A Stack (when empty)" should "be empty" in { assert(emptyStack.empty) } it should "complain on peek" in { assertThrows[IllegalStateException] { emptyStack.peek } } it should "complain on pop" in { assertThrows[IllegalStateException] { emptyStack.pop } } "A Stack (with one item)" should behave like nonEmptyStack(stackWithOneItem, lastValuePushed) it should behave like nonFullStack(stackWithOneItem) "A Stack (with one item less than capacity)" should behave like nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed) it should behave like nonFullStack(stackWithOneItemLessThanCapacity) "A Stack (full)" should "be full" in { assert(fullStack.full) } it should behave like nonEmptyStack(fullStack, lastValuePushed) it should "complain on a push" in { assertThrows[IllegalStateException] { fullStack.push(10) } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new SharedTestExampleSpec) A Stack (when empty) - should be empty - should complain on peek - should complain on pop A Stack (with one item) - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push A Stack (with one item less than capacity) - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push A Stack (full) - should be full - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. A good way to solve this problem in a
FlatSpec
is to make sure each invocation of a behavior function is in the context of a different set ofwhen
, verb (should
,must
, or can), andthat
clauses, which will prepend a string to each test name. For example, the following code in aFlatSpec
would register a test with the name"A Stack (when empty) should be empty"
:behavior of "A Stack (when empty)" it should "be empty" in { assert(emptyStack.empty) } // ...
Or, using the shorthand notation:
"A Stack" when { "empty" should { "be empty" in { assert(emptyStack.empty) } } } // ...
If the
"should be empty"
test was factored out into a behavior function, it could be called repeatedly so long as each invocation of the behavior function is in the context of a different combination ofwhen
, verb, andthat
clauses. -
trait
FlatSpecLike
extends TestSuite with TestRegistration with ShouldVerb with MustVerb with CanVerb with Informing with Notifying with Alerting with Documenting
Implementation trait for class
FlatSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
FlatSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.FlatSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofFlatSpec
into some other class, you can use this trait instead, because classFlatSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
FlatSpec
. -
class
FreeSpec
extends FreeSpecLike
Facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (
-
).Facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (
-
).FreeSpec
is so named because unlike classes such asWordSpec
,FlatSpec
, andFunSpec
, it is enforces no structure on the text. You are free to compose text however you like. (AFreeSpec
is like free-verse poetry as opposed to a sonnet or haiku, which defines a structure for the text of the poem.)Recommended Usage: Because it gives absolute freedom (and no guidance) on how specification text should be written, FreeSpec
is a good choice for teams experienced with BDD and able to agree on how to structure the specification text.Here's an example
FreeSpec
:package org.scalatest.examples.freespec import org.scalatest.FreeSpec class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" in { assert(Set.empty.size === 0) } "should produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
In a
FreeSpec
you write a test with a string followed byin
and the body of the test in curly braces, like this:"should have size 0" in { // ... }
You can nest a test inside any number of description clauses, which you write with a string followed by a dash character and a block, like this:
"A Set" - { // ... }
You can nest description clauses as deeply as you want. Because the description clause is denoted with an operator, not a word like
should
, you are free to structure the text however you wish. Here's an example:import org.scalatest.FreeSpec class StackSpec extends FreeSpec { "A Stack" - { "whenever it is empty" - { "certainly ought to" - { "be empty" in { // ... } "complain on peek" in { // ... } "complain on pop" in { // ... } } } "but when full, by contrast, must" - { "be full" in { // ... } "complain on push" in { // ... } } } }
Running the above
StackSpec
in the interpreter would yield:scala> org.scalatest.run(new StackSpec) StackSpec: A Stack whenever it is empty certainly ought to - be empty - complain on peek - complain on pop but when full, by contrast, must - be full - complain on push
A
FreeSpec
can also be used to write a specification-style test in languages other than English. For example:import org.scalatest.FreeSpec class ComputerRoomRulesSpec extends FreeSpec { "Achtung!" - { "Alle touristen und non-technischen lookenpeepers!" - { "Das machine is nicht fuer fingerpoken und mittengrabben." in { // ... } "Is easy" - { "schnappen der springenwerk" in { // ... } "blowenfusen" in { // ... } "und poppencorken mit spitzen sparken." in { // ... } } "Das machine is diggen by experten only." in { // ... } "Is nicht fuer gerwerken by das dummkopfen." in { // ... } "Das rubbernecken sightseeren keepen das cottenpicken hands in das pockets." in { // ... } "Relaxen und watchen das blinkenlights." in { // ... } } } }
Running the above
ComputerRoomRulesSpec
in the interpreter would yield:scala> org.scalatest.run(new ComputerRoomRulesSpec) ComputerRoomRulesSpec: Achtung! Alle touristen und non-technischen lookenpeepers! - Das machine is nicht fuer fingerpoken und mittengrabben. Is easy - schnappen der springenwerk - blowenfusen - und poppencorken mit spitzen sparken. - Das machine is diggen by experten only. - Is nicht fuer gerwerken by das dummkopfen. - Das rubbernecken sightseeren keepen das cottenpicken hands in das pockets. - Relaxen und watchen das blinkenlights.
A
FreeSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered while the
FreeSpec
is in its registration phase. Any attempt to register a test after theFreeSpec
has entered its ready phase, i.e., afterrun
has been invoked on theFreeSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingFreeSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.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,
FreeSpec
adds a methodignore
to strings that can be used instead ofin
to register a test. For example, to temporarily disable the test with the name"A Stack should pop values in last-in-first-out order"
, just change “in
” into “ignore
,” like this:package org.scalatest.examples.freespec.ignore import org.scalatest.FreeSpec class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" ignore { assert(Set.empty.size === 0) } "should produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run only the second test and report that the first test was ignored:
A Set when empty - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.freespec.ignoreall import org.scalatest.FreeSpec import org.scalatest.Ignore @Ignore class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" in { assert(Set.empty.size === 0) } "should produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSpec
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSpec) SetSpec: A Set when empty - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.Informers
One of the parameters to
FreeSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byFreeSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byFreeSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.freespec.info import collection.mutable import org.scalatest._ class SetSpec extends FreeSpec with GivenWhenThen { "A mutable Set" - { "should allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } } }
If you run this
FreeSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
FreeSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FreeSpec
that usesmarkup
:package org.scalatest.examples.freespec.markup import collection.mutable import org.scalatest._ class SetSpec extends FreeSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" - { "should allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.freespec.note import collection.mutable import org.scalatest._ class SetSpec extends FreeSpec { "A mutable Set" - { "should allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, 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. You can mark tests as pending in aFreeSpec
like this:package org.scalatest.examples.freespec.pending import org.scalatest._ class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" in (pending) "should produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run both tests but report that
should have size 0
is pending. You'll see:A Set when empty - should have size 0 (pending) - should produce NoSuchElementException when head is invoked
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait. For example, the following snippet in aFreeSpec
:"The Scala language" - { "should add correctly" in { Given("two integers") When("they are added") Then("the result is the sum of the two numbers") pending } // ...
Would yield the following output when run in the interpreter:
The Scala language - should add correctly (pending) + Given two integers + When they are added + Then the result is the sum of the two numbers
Tagging tests
A
FreeSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aFreeSpec
, groups of tests can optionally be included and/or excluded. To tag aFreeSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forFreeSpec
s like this:import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could tag
FreeSpec
tests like this:package org.scalatest.examples.freespec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest") import org.scalatest.FreeSpec import org.scalatest.tagobjects.Slow class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" taggedAs(Slow) in { assert(Set.empty.size === 0) } "should produce NoSuchElementException when head is invoked" taggedAs(Slow, DbTest) in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aFreeSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or an holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.freespec.getfixture import org.scalatest.FreeSpec import collection.mutable.ListBuffer class ExampleSpec extends FreeSpec { class Fixture { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] } def fixture = new Fixture "Testing" - { "should be easy" in { val f = fixture f.builder.append("easy!") assert(f.builder.toString === "ScalaTest is easy!") assert(f.buffer.isEmpty) f.buffer += "sweet" } "should be fun" in { val f = fixture f.builder.append("fun!") assert(f.builder.toString === "ScalaTest is fun!") assert(f.buffer.isEmpty) } } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, if you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.freespec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FreeSpec class ExampleSpec extends FreeSpec { trait Builder { val builder = new StringBuilder("ScalaTest is ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is") } "Testing" - { // This test needs the StringBuilder fixture "should be productive" in new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } } "Test code" - { // This test needs the ListBuffer[String] fixture "should be readable" in new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } // This test needs both the StringBuilder and ListBuffer "should be clear and concise" in new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.freespec.noargtest import java.io.File import org.scalatest._ class ExampleSpec extends FreeSpec { override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } "This test" - { "should succeed" in { assert(1 + 1 === 2) } "should fail" in { assert(1 + 1 === 3) } } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.freespec.loanfixture import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.FreeSpec import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSpec extends FreeSpec { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } "Testing" - { // This test needs the file fixture "should be productive" in withFile { (file, writer) => writer.write("productive!") writer.flush() assert(file.length === 24) } } "Test code" - { // This test needs the database fixture "should be readable" in withDatabase { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } // This test needs both the file and the database "should be clear and concise" in withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("clear!") writer.write("concise!") writer.flush() assert(db.toString === "ScalaTest is clear!") assert(file.length === 21) } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.FreeSpec
and overridingwithFixture(OneArgTest)
. Each test in afixture.FreeSpec
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function.To enable the stacking of traits that define
withFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:
package org.scalatest.examples.freespec.oneargtest import org.scalatest.fixture import java.io._ class ExampleSpec extends fixture.FreeSpec { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { // create the fixture val file = File.createTempFile("hello", "world") val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } "Testing" - { "should be easy" in { f => f.writer.write("easy!") f.writer.flush() assert(f.file.length === 18) } "should be fun" in { f => f.writer.write("fun!") f.writer.flush() assert(f.file.length === 17) } } }
In this example, the tests actually required two fixture objects, a
File
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.FreeSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.freespec.beforeandafter import org.scalatest.FreeSpec import org.scalatest.BeforeAndAfter import collection.mutable.ListBuffer class ExampleSpec extends FreeSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } "Testing" - { "should be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "should be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.freespec.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSpec extends FreeSpec with Builder with Buffer { "Testing" - { "should be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "should be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends FreeSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Spec extends FreeSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.freespec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally buffer.clear() } } class ExampleSpec extends FreeSpec with Builder with Buffer { "Testing" - { "should be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "should be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in a
FreeSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyFreeSpec
that uses them, so that the tests they contain will be registered as tests in thatFreeSpec
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test the
Stack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourFreeSpec
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures. You can define a behavior function that encapsulates these shared tests inside theFreeSpec
that uses them. If they are shared between differentFreeSpec
s, however, you could also define them in a separate trait that is mixed into eachFreeSpec
that uses them.For example, here the
nonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:trait StackBehaviors { this: FreeSpec => def nonEmptyStack(newStack: => Stack[Int], lastItemAdded: Int) { "be non-empty" in { assert(!newStack.empty) } "return the top item on peek" in { assert(newStack.peek === lastItemAdded) } "not remove the top item on peek" in { val stack = newStack val size = stack.size assert(stack.peek === lastItemAdded) assert(stack.size === size) } "remove the top item on pop" in { val stack = newStack val size = stack.size assert(stack.pop === lastItemAdded) assert(stack.size === size - 1) } } def nonFullStack(newStack: => Stack[Int]) { "not be full" in { assert(!newStack.full) } "add to the top on push" in { val stack = newStack val size = stack.size stack.push(7) assert(stack.size === size + 1) assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, but
FreeSpec
offers a DSL for the purpose, which looks like this:behave like nonEmptyStack(stackWithOneItem, lastValuePushed) behave like nonFullStack(stackWithOneItem)
If you prefer to use an imperative style to change fixtures, for example by mixing in
BeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:behave like nonEmptyStack // assuming lastValuePushed is also in scope inside nonEmptyStack behave like nonFullStack
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:
class SharedTestExampleSpec extends FreeSpec with StackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 "A Stack" - { "when empty" - { "should be empty" in { assert(emptyStack.empty) } "should complain on peek" in { assertThrows[IllegalStateException] { emptyStack.peek } } "should complain on pop" in { assertThrows[IllegalStateException] { emptyStack.pop } } } "when it contains one item" - { "should" - { behave like nonEmptyStack(stackWithOneItem, lastValuePushed) behave like nonFullStack(stackWithOneItem) } } "when it contains one item less than capacity" - { "should" - { behave like nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed) behave like nonFullStack(stackWithOneItemLessThanCapacity) } } "when full" - { "should be full" in { assert(fullStack.full) } "should" - { behave like nonEmptyStack(fullStack, lastValuePushed) } "should complain on a push" in { assertThrows[IllegalStateException] { fullStack.push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new SharedTestExampleSpec) SharedTestExampleSpec: A Stack when empty - should be empty - should complain on peek - should complain on pop when it contains one item should - be non-empty - return the top item on peek - not remove the top item on peek - remove the top item on pop - not be full - add to the top on push when it contains one item less than capacity should - be non-empty - return the top item on peek - not remove the top item on peek - remove the top item on pop - not be full - add to the top on push when full - should be full should - be non-empty - return the top item on peek - not remove the top item on peek - remove the top item on pop - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. A good way to solve this problem in a
FreeSpec
is to make sure each test is in the context of different surrounding description clauses, because a test's name is the concatenation of its surrounding clauses, followed by the test's text. For example, the following code in aFreeSpec
would register a test with the name"A Stack when empty should be empty"
:"A Stack" - { "when empty" - { "should be empty" in { assert(emptyStack.empty) } } } // ...
If the
"should be empty"
test was factored out into a behavior function, it could be called repeatedly so long as each invocation of the behavior function is in the context of a different surrounding description (dash) clauses. -
trait
FreeSpecLike
extends TestSuite with TestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
FreeSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (-
).Implementation trait for class
FreeSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are nested inside text clauses denoted with the dash operator (-
).FreeSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofFreeSpec
into some other class, you can use this trait instead, because classFreeSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
FreeSpec
. -
class
FunSpec
extends FunSpecLike
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Recommended Usage: For teams coming from Ruby's RSpec tool, FunSpec
will feel familiar and comfortable; More generally, for any team that prefers BDD,FunSpec
's nesting and gentle guide to structuring text (withdescribe
andit
) provide an excellent general-purpose choice for writing specification-style tests.Here's an example
FunSpec
:package org.scalatest.examples.funspec import org.scalatest.FunSpec class SetSpec extends FunSpec { describe("A Set") { describe("when empty") { it("should have size 0") { assert(Set.empty.size === 0) } it("should produce NoSuchElementException when head is invoked") { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
A
FunSpec
contains describe clauses and tests. You define a describe clause withdescribe
, and a test with eitherit
orthey
.describe
,it
, andthey
are methods, defined inFunSpec
, which will be invoked by the primary constructor ofSetSpec
. A describe clause names, or gives more information about, the subject (class or other entity) you are specifying and testing. In the previous example,"A Set"
is the subject under specification and test. With each test you provide a string (the spec text) that specifies one bit of behavior of the subject, and a block of code that tests that behavior. You place the spec text between the parentheses, followed by the test code between curly braces. The test code will be wrapped up as a function passed as a by-name parameter toit
(orthey
), which will register the test for later execution.Note: the
they
method is intended for use when the subject is plural, for example:describe("The combinators") { they("should be easy to learn") {} they("should be efficient") {} they("should do something cool") {} }
A
FunSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
it
orthey
methods while theFunSpec
is in its registration phase. Any attempt to register a test after theFunSpec
has entered its ready phase, i.e., afterrun
has been invoked on theFunSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingFunSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.When you execute a
FunSpec
, it will sendFormatter
s in the events it sends to theReporter
. ScalaTest's built-in reporters will report these events in such a way that the output is easy to read as an informal specification of the subject being tested. For example, were you to runSetSpec
from within the Scala interpreter:scala> org.scalatest.run(new SetSpec)
You would see:
A Set when empty - should have size 0 - should produce NoSuchElementException when head is invoked
Or, to run just the “
A Set when empty should have size 0
” test, you could pass that test's name, or any unique substring of the name, such as"size 0"
or even just"0"
. Here's an example:scala> org.scalatest.run(new SetSuite, "size 0") A Set when empty - should have size 0
You can also pass to
execute
a config map of key-value pairs, which will be passed down into suites and tests, as well as other parameters that configure the run itself. For more information on running in the Scala interpreter, see the documentation forexecute
(below) and the ScalaTest shell.The
execute
method invokes arun
method that takes two parameters. Thisrun
method, which actually executes the suite, will usually be invoked by a test runner, such asrun
,tools.Runner
, a build tool, or an IDE.Note:
FunSpec
's syntax is in great part inspired by RSpec, a Ruby BDD framework.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,
FunSpec
provides registration methods that start withignore
instead ofit
orthey
. For example, to temporarily disable the test with the text"should have size 0"
, just change “it
” into “ignore
,” like this:package org.scalatest.examples.funspec.ignore import org.scalatest.FunSpec class SetSpec extends FunSpec { describe("A Set") { describe("when empty") { ignore("should have size 0") { assert(Set.empty.size === 0) } it("should produce NoSuchElementException when head is invoked") { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run only the second test and report that the first test was ignored:
A Set when empty - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.funspec.ignoreall import org.scalatest.FunSpec import org.scalatest.Ignore @Ignore class SetSpec extends FunSpec { describe("A Set") { describe("when empty") { it("should have size 0") { assert(Set.empty.size === 0) } it("should produce NoSuchElementException when head is invoked") { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSpec
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSpec) SetSpec: A Set when empty - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.Informers
One of the parameters to
FunSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byFunSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via one of itsapply
methods. TheInformer
will then pass the information to theReporter
via anInfoProvided
event. Here's an example in which theInformer
returned byinfo
is used implicitly by theGiven
,When
, andThen
methods of traitGivenWhenThen
:package org.scalatest.examples.funspec.info import collection.mutable import org.scalatest._ class SetSpec extends FunSpec with GivenWhenThen { describe("A mutable Set") { it("should allow an element to be added") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } } }
If you run this
FunSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
FunSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FunSpec
that usesmarkup
:package org.scalatest.examples.funspec.markup import collection.mutable import org.scalatest._ class SetSpec extends FunSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } describe("A mutable Set") { it("should allow an element to be added") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.funspec.note import collection.mutable import org.scalatest._ class SetSpec extends FunSpec { describe("A mutable Set") { it("should allow an element to be added") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented.You can mark a test as pending in
FunSpec
by placing "(pending)
" after the test name, like this:package org.scalatest.examples.funspec.pending import org.scalatest._ class SetSpec extends FunSpec { describe("A Set") { describe("when empty") { it("should have size 0") (pending) it("should produce NoSuchElementException when head is invoked") { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofSetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run both tests, but report that the test named "
should have size 0
" is pending. You'll see:A Set when empty - should have size 0 (pending) - should produce NoSuchElementException when head is invoked
Tagging tests
A
FunSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aFunSpec
, groups of tests can optionally be included and/or excluded. To tag aFunSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forFunSpec
s like this:package org.scalatest.examples.funspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
FunSpec
tests into groups with tags like this:import org.scalatest.FunSpec import org.scalatest.tagobjects.Slow class SetSpec extends FunSpec { describe("A Set") { describe("when empty") { it("should have size 0", Slow) { assert(Set.empty.size === 0) } it("should produce NoSuchElementException when head is invoked", Slow, DbTest) { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aFunSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or an holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.funspec.getfixture import org.scalatest.FunSpec import collection.mutable.ListBuffer class ExampleSpec extends FunSpec { class Fixture { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] } def fixture = new Fixture describe("Testing") { it("should be easy") { val f = fixture f.builder.append("easy!") assert(f.builder.toString === "ScalaTest is easy!") assert(f.buffer.isEmpty) f.buffer += "sweet" } it("should be fun") { val f = fixture f.builder.append("fun!") assert(f.builder.toString === "ScalaTest is fun!") assert(f.buffer.isEmpty) } } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, if you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.funspec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FunSpec class ExampleSpec extends FunSpec { trait Builder { val builder = new StringBuilder("ScalaTest is ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is") } describe("Testing") { // This test needs the StringBuilder fixture it("should be productive") { new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } } } describe("Test code") { // This test needs the ListBuffer[String] fixture it("should be readable") { new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } } // This test needs both the StringBuilder and ListBuffer it("should be clear and concise") { new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } } } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.funspec.noargtest import java.io.File import org.scalatest._ class ExampleSpec extends FunSpec { override def withFixture(test: NoArgTest) = { try super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } describe("This test") { it("should succeed") { assert(1 + 1 === 2) } it("should fail") { assert(1 + 1 === 3) } } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.funspec.loanfixture import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.FunSpec import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSpec extends FunSpec { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } describe("Testing") { // This test needs the file fixture it("should be productive") { withFile { (file, writer) => writer.write("productive!") writer.flush() assert(file.length === 24) } } } describe("Test code") { // This test needs the database fixture it("should be readable") { withDatabase { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } } // This test needs both the file and the database it("should be clear and concise") { withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("clear!") writer.write("concise!") writer.flush() assert(db.toString === "ScalaTest is clear!") assert(file.length === 21) } } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.Suite
and overridingwithFixture(OneArgTest)
. Each test in afixture.Suite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function.To enable the stacking of traits that define
withFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:
package org.scalatest.examples.funspec.oneargtest import org.scalatest.fixture import java.io._ class ExampleSpec extends fixture.FunSpec { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { // create the fixture val file = File.createTempFile("hello", "world") val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } describe("Testing") { it("should be easy") { f => f.writer.write("easy!") f.writer.flush() assert(f.file.length === 18) } it("should be fun") { f => f.writer.write("fun!") f.writer.flush() assert(f.file.length === 17) } } }
In this example, the tests actually required two fixture objects, a
File
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.FunSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.funspec.beforeandafter import org.scalatest.FunSpec import org.scalatest.BeforeAndAfter import collection.mutable.ListBuffer class ExampleSpec extends FunSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } describe("Testing") { it("should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it("should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.funspec.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSpec extends FunSpec with Builder with Buffer { describe("Testing") { it("should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it("should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends FunSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Spec extends FunSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.funspec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally buffer.clear() } } class ExampleSpec extends FunSpec with Builder with Buffer { describe("Testing") { it("should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } it("should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in a
FunSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyFunSpec
that uses them, so that the tests they contain will be registered as tests in thatFunSpec
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test the
Stack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourFunSpec
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures. You can define a behavior function that encapsulates these shared tests inside theFunSpec
that uses them. If they are shared between differentFunSpec
s, however, you could also define them in a separate trait that is mixed into eachFunSpec
that uses them.For example, here the
nonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:trait StackBehaviors { this: FunSpec => def nonEmptyStack(newStack: => Stack[Int], lastItemAdded: Int) { it("should be non-empty") { assert(!newStack.empty) } it("should return the top item on peek") { assert(newStack.peek === lastItemAdded) } it("should not remove the top item on peek") { val stack = newStack val size = stack.size assert(stack.peek === lastItemAdded) assert(stack.size === size) } it("should remove the top item on pop") { val stack = newStack val size = stack.size assert(stack.pop === lastItemAdded) assert(stack.size === size - 1) } } def nonFullStack(newStack: => Stack[Int]) { it("should not be full") { assert(!newStack.full) } it("should add to the top on push") { val stack = newStack val size = stack.size stack.push(7) assert(stack.size === size + 1) assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, but
FunSpec
offers a DSL for the purpose, which looks like this:it should behave like nonEmptyStack(stackWithOneItem, lastValuePushed) it should behave like nonFullStack(stackWithOneItem)
If you prefer to use an imperative style to change fixtures, for example by mixing in
BeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:it should behave like nonEmptyStack // assuming lastValuePushed is also in scope inside nonEmptyStack it should behave like nonFullStack
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:
class SharedTestExampleSpec extends FunSpec with StackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 describe("A Stack") { describe("(when empty)") { it("should be empty") { assert(emptyStack.empty) } it("should complain on peek") { assertThrows[IllegalStateException] { emptyStack.peek } } it("should complain on pop") { assertThrows[IllegalStateException] { emptyStack.pop } } } describe("(with one item)") { it should behave like nonEmptyStack(stackWithOneItem, lastValuePushed) it should behave like nonFullStack(stackWithOneItem) } describe("(with one item less than capacity)") { it should behave like nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed) it should behave like nonFullStack(stackWithOneItemLessThanCapacity) } describe("(full)") { it("should be full") { assert(fullStack.full) } it should behave like nonEmptyStack(fullStack, lastValuePushed) it("should complain on a push") { assertThrows[IllegalStateException] { fullStack.push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackSpec) A Stack (when empty) - should be empty - should complain on peek - should complain on pop A Stack (with one item) - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push A Stack (with one item less than capacity) - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push A Stack (full) - should be full - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. A good way to solve this problem in a
FunSpec
is to surround each invocation of a behavior function with adescribe
clause, which will prepend a string to each test name. For example, the following code in aFunSpec
would register a test with the name"A Stack (when empty) should be empty"
:describe("A Stack") { describe("(when empty)") { it("should be empty") { assert(emptyStack.empty) } // ...
If the
"should be empty"
test was factored out into a behavior function, it could be called repeatedly so long as each invocation of the behavior function is inside a different set ofdescribe
clauses. -
trait
FunSpecLike
extends TestSuite with TestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
FunSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
FunSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.FunSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofFunSpec
into some other class, you can use this trait instead, because classFunSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
FunSpec
. -
class
FunSuite
extends FunSuiteLike
A suite of tests in which each test is represented as a function value.
A suite of tests in which each test is represented as a function value. The “
Fun
” inFunSuite
stands for “function.”Recommended Usage: For teams coming from xUnit, FunSuite
feels comfortable and familiar while still giving some benefits of BDD:FunSuite
makes it easy to write descriptive test names, natural to write focused tests, and generates specification-like output that can facilitate communication among stakeholders.Here's an example
FunSuite
:package org.scalatest.examples.funsuite import org.scalatest.FunSuite class SetSuite extends FunSuite { test("An empty Set should have size 0") { assert(Set.empty.size === 0) } test("Invoking head on an empty Set should produce NoSuchElementException") { assertThrows[NoSuchElementException] { Set.empty.head } } }
“
test
” is a method, defined inFunSuite
, which will be invoked by the primary constructor ofSetSuite
. 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 totest
, which registers it for later execution.A
FunSuite
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
test
method while theFunSuite
is in its registration phase. Any attempt to register a test after theFunSuite
has entered its ready phase, i.e., afterrun
has been invoked on theFunSuite
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingFunSuite
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Note:
FunSuite
was in part inspired by Rehersal, an early test framework for Scala.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 withignore
instead oftest
. Here's an example:package org.scalatest.examples.funsuite.ignore import org.scalatest.FunSuite class SetSuite extends FunSuite { ignore("An empty Set should have size 0") { assert(Set.empty.size === 0) } test("Invoking head on an empty Set should produce NoSuchElementException") { assertThrows[NoSuchElementException] { Set.empty.head } } }
If you run this version of
SetSuite
with:scala> org.scalatest.run(new SetSuite)
It will run only the second test and report that the first test was ignored:
SetSuite: - An empty Set should have size 0 !!! IGNORED !!! - Invoking head on an empty Set should produce NoSuchElementException
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.funsuite.ignoreall import org.scalatest.FunSuite import org.scalatest.Ignore @Ignore class SetSuite extends FunSuite { test("An empty Set should have size 0") { assert(Set.empty.size === 0) } test("Invoking head on an empty Set should produce NoSuchElementException") { assertThrows[NoSuchElementException] { Set.empty.head } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSuite
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSuite
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSuite) SetSuite: - An empty Set should have size 0 !!! IGNORED !!! - Invoking head on an empty Set should produce NoSuchElementException !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.Informers
One of the parameters to
FunSuite
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byFunSuite
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event. Here's an example that shows both a direct use as well as an indirect use through the methods ofGivenWhenThen
:package org.scalatest.examples.funsuite.info import collection.mutable import org.scalatest._ class SetSuite extends FunSuite with GivenWhenThen { test("An element can be added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } }
If you run this
FunSuite
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSuite) SetSuite: - an element can be added to an empty mutable Set + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
FunSuite
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
FunSuite
that usesmarkup
:package org.scalatest.examples.funsuite.markup import collection.mutable import org.scalatest._ class SetSuite extends FunSuite with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } test("An element can be added to an empty mutable Set") { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.funsuite.note import collection.mutable import org.scalatest._ class SetSuite extends FunSuite { test("An element can be added to an empty mutable Set") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSuite: + notes are sent immediately + alerts are also sent immediately - An element can be added to an empty mutable Set + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented.Although pending tests may be used more often in specification-style suites, such as
org.scalatest.FunSpec
, you can also use it inFunSuite
, like this:package org.scalatest.examples.funsuite.pending import org.scalatest._ class SetSuite extends FunSuite { test("An empty Set should have size 0") (pending) test("Invoking head on an empty Set should produce NoSuchElementException") { assertThrows[NoSuchElementException] { Set.empty.head } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofSetSuite
with:scala> org.scalatest.run(new SetSuite)
It will run both tests, but report that first test is pending. You'll see:
SetSuite: - An empty Set should have size 0 (pending) - Invoking head on an empty Set should produce NoSuchElementException
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait.Tagging tests
A
FunSuite
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aFunSuite
, groups of tests can optionally be included and/or excluded. To tag aFunSuite
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forFunSuite
s like this:package org.scalatest.examples.funsuite.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
FunSuite
tests into groups with tags like this:import org.scalatest.FunSuite import org.scalatest.tagobjects.Slow class SetSuite extends FunSuite { test("An empty Set should have size 0", Slow) { assert(Set.empty.size === 0) } test("Invoking head on an empty Set should produce NoSuchElementException", Slow, DbTest) { assertThrows[NoSuchElementException] { Set.empty.head } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aFunSuite
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or an holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.funsuite.getfixture import org.scalatest.FunSuite import collection.mutable.ListBuffer class ExampleSuite extends FunSuite { class Fixture { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] } def fixture = new Fixture test("Testing should be easy") { val f = fixture f.builder.append("easy!") assert(f.builder.toString === "ScalaTest is easy!") assert(f.buffer.isEmpty) f.buffer += "sweet" } test("Testing should be fun") { val f = fixture f.builder.append("fun!") assert(f.builder.toString === "ScalaTest is fun!") assert(f.buffer.isEmpty) } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, if you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.funsuite.fixturecontext import collection.mutable.ListBuffer import org.scalatest.FunSuite class ExampleSuite extends FunSuite { trait Builder { val builder = new StringBuilder("ScalaTest is ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is") } // This test needs the StringBuilder fixture test("Testing should be productive") { new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } } // This test needs the ListBuffer[String] fixture test("Test code should be readable") { new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } } // This test needs both the StringBuilder and ListBuffer test("Test code should be clear and concise") { new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.funsuite.noargtest import java.io.File import org.scalatest._ class ExampleSuite extends FunSuite { override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } test("This test should succeed") { assert(1 + 1 === 2) } test("This test should fail") { assert(1 + 1 === 3) } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: - this test should succeed - this test should fail *** FAILED *** 2 did not equal 3 (
:33) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)package org.scalatest.examples.funsuite.loanfixture import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.FunSuite import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSuite extends FunSuite { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } // This test needs the file fixture test("Testing should be productive") { withFile { (file, writer) => writer.write("productive!") writer.flush() assert(file.length === 24) } } // This test needs the database fixture test("Test code should be readable") { withDatabase { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } } // This test needs both the file and the database test("Test code should be clear and concise") { withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("clear!") writer.write("concise!") writer.flush() assert(db.toString === "ScalaTest is clear!") assert(file.length === 21) } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.Suite
and overridingwithFixture(OneArgTest)
. Each test in afixture.Suite
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function.To enable the stacking of traits that define
withFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. In other words, instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:
package org.scalatest.examples.funsuite.oneargtest import org.scalatest.fixture import java.io._ class ExampleSuite extends fixture.FunSuite { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { // create the fixture val file = File.createTempFile("hello", "world") val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } test("Testing should be easy") { f => f.writer.write("easy!") f.writer.flush() assert(f.file.length === 18) } test("Testing should be fun") { f => f.writer.write("fun!") f.writer.flush() assert(f.file.length === 17) } }
In this example, the tests actually required two fixture objects, a
File
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.FunSuite
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.funsuite.beforeandafter import org.scalatest.FunSuite import org.scalatest.BeforeAndAfter import collection.mutable.ListBuffer class ExampleSuite extends FunSuite with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } test("testing should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } test("testing should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.funsuite.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSuite extends FunSuite with Builder with Buffer { test("Testing should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } test("Testing should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } }
By mixing in both the
Builder
andBuffer
traits,ExampleSuite
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Suite extends FunSuite with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Suite extends FunSuite with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.funsuite.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try { super.afterEach() // To be stackable, must call super.afterEach } finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try { super.afterEach() // To be stackable, must call super.afterEach } finally buffer.clear() } } class ExampleSuite extends FunSuite with Builder with Buffer { test("Testing should be easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } test("Testing should be fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in a
FunSuite
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyFunSuite
that uses them, so that the tests they contain will be registered as tests in thatFunSuite
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test the
Stack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourFunSuite
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures.You can define a behavior function that encapsulates these shared tests inside the
FunSuite
that uses them. If they are shared between differentFunSuite
s, however, you could also define them in a separate trait that is mixed into eachFunSuite
that uses them. For example, here thenonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:import org.scalatest.FunSuite trait FunSuiteStackBehaviors { this: FunSuite => def nonEmptyStack(createNonEmptyStack: => Stack[Int], lastItemAdded: Int) { test("empty is invoked on this non-empty stack: " + createNonEmptyStack.toString) { val stack = createNonEmptyStack assert(!stack.empty) } test("peek is invoked on this non-empty stack: " + createNonEmptyStack.toString) { val stack = createNonEmptyStack val size = stack.size assert(stack.peek === lastItemAdded) assert(stack.size === size) } test("pop is invoked on this non-empty stack: " + createNonEmptyStack.toString) { val stack = createNonEmptyStack val size = stack.size assert(stack.pop === lastItemAdded) assert(stack.size === size - 1) } } def nonFullStack(createNonFullStack: => Stack[Int]) { test("full is invoked on this non-full stack: " + createNonFullStack.toString) { val stack = createNonFullStack assert(!stack.full) } test("push is invoked on this non-full stack: " + createNonFullStack.toString) { val stack = createNonFullStack val size = stack.size stack.push(7) assert(stack.size === size + 1) assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, but
FunSuite
offers a DSL for the purpose, which looks like this:testsFor(nonEmptyStack(stackWithOneItem, lastValuePushed)) testsFor(nonFullStack(stackWithOneItem))
If you prefer to use an imperative style to change fixtures, for example by mixing in
BeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:testsFor(nonEmptyStack) // assuming lastValuePushed is also in scope inside nonEmptyStack testsFor(nonFullStack)
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:
import org.scalatest.FunSuite class StackFunSuite extends FunSuite with FunSuiteStackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 test("empty is invoked on an empty stack") { val stack = emptyStack assert(stack.empty) } test("peek is invoked on an empty stack") { val stack = emptyStack assertThrows[IllegalStateException] { stack.peek } } test("pop is invoked on an empty stack") { val stack = emptyStack assertThrows[IllegalStateException] { stack.pop } } testsFor(nonEmptyStack(stackWithOneItem, lastValuePushed)) testsFor(nonFullStack(stackWithOneItem)) testsFor(nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed)) testsFor(nonFullStack(stackWithOneItemLessThanCapacity)) test("full is invoked on a full stack") { val stack = fullStack assert(stack.full) } testsFor(nonEmptyStack(fullStack, lastValuePushed)) test("push is invoked on a full stack") { val stack = fullStack assertThrows[IllegalStateException] { stack.push(10) } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new StackFunSuite) StackFunSuite: - empty is invoked on an empty stack - peek is invoked on an empty stack - pop is invoked on an empty stack - empty is invoked on this non-empty stack: Stack(9) - peek is invoked on this non-empty stack: Stack(9) - pop is invoked on this non-empty stack: Stack(9) - full is invoked on this non-full stack: Stack(9) - push is invoked on this non-full stack: Stack(9) - empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) - peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) - pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) - full is invoked on this non-full stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) - push is invoked on this non-full stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1) - full is invoked on a full stack - empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - push is invoked on a full stack
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. In a
FunSuite
there is no nesting construct analogous toFunSpec
'sdescribe
clause. Therefore, you need to do a bit of extra work to ensure that the test names are unique. If a duplicate test name problem shows up in aFunSuite
, you'll need to pass in a prefix or suffix string to add to each test name. You can pass this string the same way you pass any other data needed by the shared tests, or just calltoString
on the shared fixture object. This is the approach taken by the previousFunSuiteStackBehaviors
example.Given this
FunSuiteStackBehaviors
trait, calling it with thestackWithOneItem
fixture, like this:testsFor(nonEmptyStack(stackWithOneItem, lastValuePushed))
yields test names:
empty is invoked on this non-empty stack: Stack(9)
peek is invoked on this non-empty stack: Stack(9)
pop is invoked on this non-empty stack: Stack(9)
Whereas calling it with the
stackWithOneItemLessThanCapacity
fixture, like this:testsFor(nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed))
yields different test names:
empty is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
peek is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
pop is invoked on this non-empty stack: Stack(9, 8, 7, 6, 5, 4, 3, 2, 1)
-
trait
FunSuiteLike
extends TestSuite with TestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
FunSuite
, which represents a suite of tests in which each test is represented as a function value.Implementation trait for class
FunSuite
, which represents a suite of tests in which each test is represented as a function value.FunSuite
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofFunSuite
into some other class, you can use this trait instead, because classFunSuite
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
FunSuite
. -
class
FutureOutcome
extends AnyRef
Wrapper class for
Future[Outcome]
that presents a more convenient API for manipulation inwithFixture
methods in async styles.Wrapper class for
Future[Outcome]
that presents a more convenient API for manipulation inwithFixture
methods in async styles.This type serves as the result type of both test functions and
withFixture
methods in ScalaTest's async styles. AFuture[Outcome]
is not used as this result type for two reasons. First,Outcome
treats exceptions specially, and as a result methods onFuture
would usually not yield the desiredFuture[Outcome]
result. Only run-aborting exceptions should result in a failedFuture[Outcome]
. Any other thrown exception other thanTestCanceledException
orTestPendingException
should result in a successfulFuture
containing aorg.scalatest.Failed
. A thrownTestCanceledException
should result in a successfulFuture
containing anorg.scalatest.Canceled
; A thrownTestPendingException
should result in a successfulFuture
containing aorg.scalatest.Pending
. If manipulating aFuture[Outcome]
directly, by contrast, any thrown exception would result in a failedFuture
.Additionally, to be consistent with corresponding transformations in traditional testing styles, methods registering callbacks should return a new future outcome that doesn't complete until both the original future outcome has completed and the subsequent callback has completed execution. Additionally, if the callback itself throws an exception, that exception should determine the result of the future outcome returned by the callback registration method. This behavior is rather inconvenient to obtain on the current
Future
API, soFutureOutcome
provides well-named methods that have this behavior.Lastly, the
FutureOutcome
is intended to help prevent confusion by eliminating the need to work with types likescala.util.Success(org.scalatest.Failed)
. For this purpose aorg.scalactic.Or
is used instead of ascala.util.Try
to describe results ofFutureOutcome
.A
FutureOutcome
represents a computation that can result in anOutcome
or an "abort." An abort means that a run-aborting exception occurred during the computation. Any other, non-run-aborting exception will be represented as an non-Succeeded
Outcome
: one ofFailed
,Canceled
, orPending
.The methods of
FutureOutcome
include the following callback registration methods:onSucceededThen
- registers a callback to be executed if the future outcome isSucceeded
.onFailedThen
- registers a callback to be executed if the future outcome isFailed
.onCanceledThen
- registers a callback to be executed if the future outcome isCanceled
.onPendingThen
- registers a callback to be executed if the future outcome isPending
.onOutcomeThen
- registers a callback to be executed if the future outcome is actually anOutcome
and not an abort.onAbortedThen
- registers a callback to be executed if the future outcome aborts.onCompletedThen
- registers a callback to be executed upon completion no matter how the future outcome completes.
The callback methods listed previously can be used to perform a side effect once a
FutureOutcome
completes. To change anOutcome
into a differentOutcome
asynchronously, use thechange
registration method, which takes a function fromOutcome
toOutcome
. The other methods onFutureOutcome
,isCompleted
andvalue
, allow you to poll aFutureOutcome
. None of the methods onFutureOutcome
block. Lastly, because an implicitFuturistic
instance is provided forFutureOutcome
, you can usecomplete
-lastly
syntax withFutureOutcome
. -
trait
GivenWhenThen
extends AnyRef
Trait that contains methods named
given
,when
,then
, andand
, which take a string message and implicitInformer
, and forward the message to the informer.Trait that contains methods named
given
,when
,then
, andand
, which take a string message and implicitInformer
, and forward the message to the informer.Here's an example:
package org.scalatest.examples.flatspec.info import collection.mutable import org.scalatest._ class SetSpec extends FlatSpec with GivenWhenThen { "A mutable Set" should "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } }
If you run this
SetSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
-
trait
Ignore
extends Annotation
Annotation used to tag a test, or suite of tests, as ignored.
Annotation used to tag a test, or suite of tests, as ignored.
Note: This is actually an annotation defined in Java, not a Scala trait. It must be defined in Java instead of Scala so it will be accessible at runtime. It has been inserted into Scaladoc by pretending it is a trait.
If you wish to temporarily ignore an entire suite of tests, you can annotate the test class with
@Ignore
, like this:package org.scalatest.examples.flatspec.ignoreall import org.scalatest._ @Ignore class SetSpec extends FlatSpec { "An empty Set" should "have size 0" in { assert(Set.empty.size === 0) } it should "produce NoSuchElementException when head is invoked" in { intercept[NoSuchElementException] { Set.empty.head } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSpec
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSpec) An empty Set - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class somewhat visible, to encourage the developers to eventually fix and un-ignore it. If you want to prevent a class from being discovered at all, use the
DoNotDiscover
annotation instead.Another use case for
@Ignore
is to mark test methods as ignored in traitsSpec
andfixture.Spec
. Here's an example:package org.scalatest.examples.spec.ignore import org.scalatest._ class SetSpec extends RefSpec { @Ignore def `an empty Set should have size 0` { assert(Set.empty.size === 0) } def `invoking head on an empty Set should produce NoSuchElementException` { intercept[NoSuchElementException] { Set.empty.head } } }
If you run this version of
SetSpec
in the Scala interpreter, you'll see that it runs only the second test and reports that the first test was ignored:scala> org.scalatest.run(new SetSpec) SetSpec: - an empty Set should have size 0 !!! IGNORED !!! - invoking head on an empty Set should produce NoSuchElementException
Note that because reflection is not supported on Scala.js, this annotation will only work on the JVM, not on Scala.js. -
trait
Informer
extends AnyRef
Trait to which custom information about a running suite of tests can be reported.
Trait to which custom information about a running suite of tests can be reported.
An
Informer
is essentially used to wrap aReporter
and provide easy ways to send custom information to thatReporter
via anInfoProvided
event.Informer
contains anapply
method that takes a string and an optional payload object of typeAny
. TheInformer
will forward the passedmessage
string to theReporter
as themessage
parameter, and the optional payload object as thepayload
parameter, of anInfoProvided
event.Here's an example in which the
Informer
is used both directly viainfo
method of traitFlatSpec
and indirectly via the methods of traitGivenWhenThen
:package org.scalatest.examples.flatspec.info import collection.mutable import org.scalatest._ class SetSpec extends FlatSpec with GivenWhenThen { "A mutable Set" should "allow an element to be added" in { given("an empty mutable Set") val set = mutable.Set.empty[String] when("an element is added") set += "clarity" then("the Set should have size 1") assert(set.size === 1) and("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } }
If you run this
SetSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
-
trait
Informing
extends AnyRef
Trait that contains the
info
method, which can be used to send info to the reporter.Trait that contains the
info
method, which can be used to send info to the reporter. -
trait
Inside
extends AnyRef
Trait containing the
inside
construct, which allows you to make statements about nested object graphs using pattern matching.Trait containing the
inside
construct, which allows you to make statements about nested object graphs using pattern matching.For example, given the following case classes:
case class Address(street: String, city: String, state: String, zip: String) case class Name(first: String, middle: String, last: String) case class Record(name: Name, address: Address, age: Int)
You could write:
inside (rec) { case Record(name, address, age) => inside (name) { case Name(first, middle, last) => first should be ("Sally") middle should be ("Ann") last should be ("Jones") } inside (address) { case Address(street, city, state, zip) => street should startWith ("25") city should endWith ("Angeles") state should equal ("CA") zip should be ("12345") } age should be < 99 }
If an assertion fails, the error message will include the
toString
of each value passed toinside
clauses enclosing the failed assertion. For example, ifrec
in the previous expression was defined like this:val rec = Record( Name("Sally", "Anna", "Jones"), Address("25 Main St", "Los Angeles", "CA", "12345"), 38 )
The error message will read:
"Ann[a]" was not equal to "Ann[]", inside Name(Sally,Anna,Jones), inside Record(Name(Sally,Anna,Jones),Address(25 Main St,Los Angeles,CA,12345),38)
-
trait
Inspectors
extends AnyRef
Provides nestable inspector methods (or just inspectors) that enable assertions to be made about collections.
Provides nestable inspector methods (or just inspectors) that enable assertions to be made about collections.
For example, the
forAll
method enables you to state that something should be true about all elements of a collection, such as that all elements should be positive:scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> import Inspectors._ import Inspectors._ scala> val xs = List(1, 2, 3, 4, 5) xs: List[Int] = List(1, 2, 3, 4, 5) scala> forAll (xs) { x => assert(x > 0) }
Or, with matchers:
scala> import Matchers._ import Matchers._ scala> forAll (xs) { x => x should be > 0 }
To make assertions about nested collections, you can nest the inspector method invocations. For example, given the following list of lists of
Int
:scala> val yss = | List( | List(1, 2, 3), | List(1, 2, 3), | List(1, 2, 3) | ) yss: List[List[Int]] = List(List(1, 2, 3), List(1, 2, 3), List(1, 2, 3))
You can assert that all
Int
elements in all nested lists are positive by nesting twoforAll
method invocations, like this:scala> forAll (yss) { ys => | forAll (ys) { y => y should be > 0 } | }
The full list of inspector methods are:
forAll
- succeeds if the assertion holds true for every elementforAtLeast
- succeeds if the assertion holds true for at least the specified number of elementsforAtMost
- succeeds if the assertion holds true for at most the specified number of elementsforBetween
- succeeds if the assertion holds true for between the specified minimum and maximum number of elements, inclusiveforEvery
- same asforAll
, but lists all failing elements if it fails (whereasforAll
just reports the first failing element)forExactly
- succeeds if the assertion holds true for exactly the specified number of elements
The error messages produced by inspector methods are designed to make sense no matter how deeply you nest the method invocations. Here's an example of a nested inspection that fails and the resulting error message:
scala> forAll (yss) { ys => | forAll (ys) { y => y should be < 2 } | } org.scalatest.exceptions.TestFailedException: forAll failed, because: at index 0, forAll failed, because: at index 1, 2 was not less than 2 (<console>:20) in List(1, 2, 3) (<console>:20) in List(List(1, 2, 3), List(1, 2, 3), List(1, 2, 3)) at org.scalatest.InspectorsHelper$.forAll(Inspectors.scala:146) ...
One way the error message is designed to help you understand the error is by using indentation that mimics the indentation of the source code (optimistically assuming the source will be nicely indented). The error message above indicates the outer
forAll
failed because its initialList
(i.e., at index 0) failed the assertion, which was that all elements of that initialList[Int]
at index 0 should be less than 2. This assertion failed because index 1 of that inner list contained the value 2, which was indeed “not less than 2.” The error message for the inner list is an indented line inside the error message for the outer list. The actual contents of each list are displayed at the end in inspector error messages, also indented appropriately. The actual contents are placed at the end so that for very large collections, the contents will not drown out and make it difficult to find the messages that describe actual causes of the failure.The
forAll
andforEvery
methods are similar in that both succeed only if the assertion holds for all elements of the collection. They differ in thatforAll
will only report the first element encountered that failed the assertion, butforEvery
will report all elements that fail the assertion. The tradeoff is that whileforEvery
gives more information, it may take longer to run because it must inspect every element of the collection. TheforAll
method can simply stop inspecting once it encounters the first failing element. Here's an example that shows the difference in theforAll
andforEvery
error messages:scala> forAll (xs) { x => x should be < 3 } org.scalatest.exceptions.TestFailedException: forAll failed, because: at index 2, 3 was not less than 3 (<console>:18) in List(1, 2, 3, 4, 5) at org.scalatest.InspectorsHelper$.forAll(Inspectors.scala:146) ... scala> forEvery (xs) { x => x should be < 3 } org.scalatest.exceptions.TestFailedException: forEvery failed, because: at index 2, 3 was not less than 3 (<console>:18), at index 3, 4 was not less than 3 (<console>:18), at index 4, 5 was not less than 3 (<console>:18) in List(1, 2, 3, 4, 5) at org.scalatest.InspectorsHelper$.forEvery(Inspectors.scala:226) ...
Note that if you're using matchers, you can alternatively use inspector shorthands for writing non-nested inspections. Here's an example:
scala> all (xs) should be > 3 org.scalatest.exceptions.TestFailedException: 'all' inspection failed, because: at index 0, 1 was not greater than 3 in List(1, 2, 3, 4, 5) at org.scalatest.InspectorsHelper$.forAll(Inspectors.scala:146)
You can use
Inspectors
on anyscala.collection.GenTraversable
,java.util.Collection
,java.util.Map
(withEntry
),Array
, orString
. Here are some examples:scala> import org.scalatest._ import org.scalatest._ scala> import Inspectors._ import Inspectors._ scala> import Matchers._ import Matchers._ scala> forAll (Array(1, 2, 3)) { e => e should be < 5 } scala> import collection.JavaConverters._ import collection.JavaConverters._ scala> val js = List(1, 2, 3).asJava js: java.util.List[Int] = [1, 2, 3] scala> forAll (js) { j => j should be < 5 } scala> val jmap = Map("a" -> 1, "b" -> 2).asJava jmap: java.util.Map[String,Int] = {a=1, b=2} scala> forAtLeast(1, jmap) { e => e shouldBe Entry("b", 2) } scala> forAtLeast(2, "hello, world!") { c => c shouldBe 'o' }
-
trait
LoneElement
extends AnyRef
Trait that provides an implicit conversion that adds to collection types a
loneElement
method, which will return the value of the lone element if the collection does indeed contain one and only one element, or throwTestFailedException
if not.Trait that provides an implicit conversion that adds to collection types a
loneElement
method, which will return the value of the lone element if the collection does indeed contain one and only one element, or throwTestFailedException
if not.This construct allows you to express in one statement that a collection should contain one and only one element and that the element value should meet some expectation. Here's an example:
set.loneElement should be > 9
Or, using an assertion instead of a matcher expression:
assert(set.loneElement > 9)
The
loneElement
syntax can be used with any collection typeC
for which an implicitCollecting[C]
is available. ScalaTest provides implicitCollecting
instances forscala.collection.GenTraversable
,Array
, andjava.util.Collection
. You can enable theloneElement
syntax on other collection types by defining an implicitCollecting
instances for those types.If you want to use
loneElement
with ajava.util.Map
, first transform it to a set of entries withentrySet
, and if helpful, use ScalaTest'sEntry
class:scala> import org.scalatest._ import org.scalatest._ scala> import LoneElement._ import LoneElement._ scala> import Matchers._ import Matchers._ scala> val jmap = new java.util.HashMap[String, Int] jmap: java.util.HashMap[String,Int] = {} scala> jmap.put("one", 1) res0: Int = 0 scala> jmap.entrySet.loneElement should be (Entry("one", 1))
-
trait
Matchers
extends Assertions with Tolerance with ShouldVerb with MatcherWords with Explicitly
Trait that provides a domain specific language (DSL) for expressing assertions in tests using the word
should
.Trait that provides a domain specific language (DSL) for expressing assertions in tests using the word
should
.For example, if you mix
Matchers
into a suite class, you can write an equality assertion in that suite like this:result should equal (3)
Here
result
is a variable, and can be of any type. If the object is anInt
with the value 3, execution will continue (i.e., the expression will result in the unit value,()
). Otherwise, aTestFailedException
will be thrown with a detail message that explains the problem, such as"7 did not equal 3"
. ThisTestFailedException
will cause the test to fail.Here is a table of contents for this documentation:
- Matchers migration in ScalaTest 2.0
- Checking equality with matchers
- Checking size and length
- Checking strings
- Greater and less than
- Checking
Boolean
properties withbe
- Using custom
BeMatchers
- Checking object identity
- Checking an object's class
- Checking numbers against a range
- Checking for emptiness
- Working with "containers"
- Working with "aggregations"
- Working with "sequences"
- Working with "sortables"
- Working with iterators
- Inspector shorthands
- Single-element collections
- Java collections and maps
String
s andArray
s as collections- Be as an equality comparison
- Being negative
- Checking that a snippet of code does not compile
- Logical expressions with
and
andor
- Working with
Option
s - Checking arbitrary properties with
have
- Using
length
andsize
withHavePropertyMatcher
s - Checking that an expression matches a pattern
- Using custom matchers
- Checking for expected exceptions
- Those pesky parens
Trait
MustMatchers
is an alternative toMatchers
that provides the exact same meaning, syntax, and behavior asMatchers
, but uses the verbmust
instead ofshould
. The two traits differ only in the English semantics of the verb:should
is informal, making the code feel like conversation between the writer and the reader;must
is more formal, making the code feel more like a written specification.Checking equality with matchers
ScalaTest matchers provides five different ways to check equality, each designed to address a different need. They are:
result should equal (3) // can customize equality result should === (3) // can customize equality and enforce type constraints result should be (3) // cannot customize equality, so fastest to compile result shouldEqual 3 // can customize equality, no parentheses required result shouldBe 3 // cannot customize equality, so fastest to compile, no parentheses required
The “
left
should
equal
(right)
” syntax requires anorg.scalactic.Equality[L]
to be provided (either implicitly or explicitly), whereL
is the left-hand type on whichshould
is invoked. In the "left
should
equal
(right)
" case, for example,L
is the type ofleft
. Thus ifleft
is typeInt
, the "left
should
equal
(right)
" statement would require anEquality[Int]
.By default, an implicit
Equality[T]
instance is available for any typeT
, in which equality is implemented by simply invoking==
on theleft
value, passing in theright
value, with special treatment for arrays. If eitherleft
orright
is an array,deep
will be invoked on it before comparing with ==. Thus, the following expression will yield false, becauseArray
'sequals
method compares object identity:Array(1, 2) == Array(1, 2) // yields false
The next expression will by default not result in a
TestFailedException
, because defaultEquality[Array[Int]]
compares the two arrays structurally, taking into consideration the equality of the array's contents:Array(1, 2) should equal (Array(1, 2)) // succeeds (i.e., does not throw TestFailedException)
If you ever do want to verify that two arrays are actually the same object (have the same identity), you can use the
be theSameInstanceAs
syntax, described below.You can customize the meaning of equality for a type when using "
should
equal
," "should
===
," orshouldEqual
syntax by defining implicitEquality
instances that will be used instead of defaultEquality
. You might do this to normalize types before comparing them with==
, for instance, or to avoid calling the==
method entirely, such as if you want to compareDouble
s with a tolerance. For an example, see the main documentation of traitEquality
.You can always supply implicit parameters explicitly, but in the case of implicit parameters of type
Equality[T]
, Scalactic provides a simple "explictly" DSL. For example, here's how you could explicitly supply anEquality[String]
instance that normalizes both left and right sides (which must be strings), by transforming them to lowercase:scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> import org.scalactic.Explicitly._ import org.scalactic.Explicitly._ scala> import org.scalactic.StringNormalizations._ import org.scalactic.StringNormalizations._ scala> "Hi" should equal ("hi") (after being lowerCased)
The
after
being
lowerCased
expression results in anEquality[String]
, which is then passed explicitly as the second curried parameter toequal
. For more information on the explictly DSL, see the main documentation for traitExplicitly
.The "
should
be
" andshouldBe
syntax do not take anEquality[T]
and can therefore not be customized. They always use the default approach to equality described above. As a result, "should
be
" andshouldBe
will likely be the fastest-compiling matcher syntax for equality comparisons, since the compiler need not search for an implicitEquality[T]
each time.The
should
===
syntax (and its complement,should
!==
) can be used to enforce type constraints at compile-time between the left and right sides of the equality comparison. Here's an example:scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> import org.scalactic.TypeCheckedTripleEquals._ import org.scalactic.TypeCheckedTripleEquals._ scala> Some(2) should === (2) <console>:17: error: types Some[Int] and Int do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Some[Int],Int] Some(2) should === (2) ^
By default, the "
Some(2)
should
===
(2)
" statement would fail at runtime. By mixing in the equality constraints provided byTypeCheckedTripleEquals
, however, the statement fails to compile. For more information and examples, see the main documentation for traitTypeCheckedTripleEquals
.Checking size and length
You can check the size or length of any type of object for which it makes sense. Here's how checking for length looks:
result should have length 3
Size is similar:
result should have size 10
The
length
syntax can be used withString
,Array
, anyscala.collection.GenSeq
, anyjava.util.List
, and any typeT
for which an implicitLength[T]
type class is available in scope. Similarly, thesize
syntax can be used withArray
, anyscala.collection.GenTraversable
, anyjava.util.Collection
, anyjava.util.Map
, and any typeT
for which an implicitSize[T]
type class is available in scope. You can enable thelength
orsize
syntax for your own arbitrary types, therefore, by definingLength
orSize
type classes for those types.In addition, the
length
syntax can be used with any object that has a field or method namedlength
or a method namedgetLength
. Similarly, thesize
syntax can be used with any object that has a field or method namedsize
or a method namedgetSize
. The type of alength
orsize
field, or return type of a method, must be eitherInt
orLong
. Any such method must take no parameters. (The Scala compiler will ensure at compile time that the object on whichshould
is being invoked has the appropriate structure.)Checking strings
You can check for whether a string starts with, ends with, or includes a substring like this:
string should startWith ("Hello") string should endWith ("world") string should include ("seven")
You can check for whether a string starts with, ends with, or includes a regular expression, like this:
string should startWith regex "Hel*o" string should endWith regex "wo.ld" string should include regex "wo.ld"
And you can check whether a string fully matches a regular expression, like this:
string should fullyMatch regex """(-)?(\d+)(\.\d*)?"""
The regular expression passed following the
regex
token can be either aString
or ascala.util.matching.Regex
.With the
startWith
,endWith
,include
, andfullyMatch
tokens can also be used with an optional specification of required groups, like this:"abbccxxx" should startWith regex ("a(b*)(c*)" withGroups ("bb", "cc")) "xxxabbcc" should endWith regex ("a(b*)(c*)" withGroups ("bb", "cc")) "xxxabbccxxx" should include regex ("a(b*)(c*)" withGroups ("bb", "cc")) "abbcc" should fullyMatch regex ("a(b*)(c*)" withGroups ("bb", "cc"))
You can check whether a string is empty with
empty
:s shouldBe empty
You can also use most of ScalaTest's matcher syntax for collections on
String
by treating theString
s as collections of characters. For examples, see theString
s andArray
s as collections section below.Greater and less than
You can check whether any type for which an implicit
Ordering[T]
is available is greater than, less than, greater than or equal, or less than or equal to a value of typeT
. The syntax is:one should be < 7 one should be > 0 one should be <= 7 one should be >= 0
Checking
Boolean
properties withbe
If an object has a method that takes no parameters and returns boolean, you can check it by placing a
Symbol
(afterbe
) that specifies the name of the method (excluding an optional prefix of "is
"). A symbol literal in Scala begins with a tick mark and ends at the first non-identifier character. Thus,'traversableAgain
results in aSymbol
object at runtime, as does'completed
and'file
. Here's an example:iter shouldBe 'traversableAgain
Given this code, ScalaTest will use reflection to look on the object referenced from
emptySet
for a method that takes no parameters and results inBoolean
, with either the nameempty
orisEmpty
. If found, it will invoke that method. If the method returnstrue
, execution will continue. But if it returnsfalse
, aTestFailedException
will be thrown that will contain a detail message, such as:non-empty iterator was not traversableAgain
This
be
syntax can be used with any reference (AnyRef
) type. If the object does not have an appropriately named predicate method, you'll get aTestFailedException
at runtime with a detailed message that explains the problem. (For the details on how a field or method is selected during this process, see the documentation forBeWord
.)If you think it reads better, you can optionally put
a
oran
afterbe
. For example,java.io.File
has two predicate methods,isFile
andisDirectory
. Thus with aFile
object namedtemp
, you could write:temp should be a 'file
Or, given
java.awt.event.KeyEvent
has a methodisActionKey
that takes no arguments and returnsBoolean
, you could assert that aKeyEvent
is an action key with:keyEvent should be an 'actionKey
If you prefer to check
Boolean
properties in a type-safe manner, you can use aBePropertyMatcher
. This would allow you to write expressions such as:xs shouldBe traversableAgain temp should be a file keyEvent should be an actionKey
These expressions would fail to compile if
should
is used on an inappropriate type, as determined by the type parameter of theBePropertyMatcher
being used. (For example,file
in this example would likely be of typeBePropertyMatcher[java.io.File]
. If used with an appropriate type, such an expression will compile and at run time theBoolean
property method or field will be accessed directly; i.e., no reflection will be used. See the documentation forBePropertyMatcher
for more information.Using custom
BeMatchers
If you want to create a new way of using
be
, which doesn't map to an actual property on the type you care about, you can create aBeMatcher
. You could use this, for example, to createBeMatcher[Int]
calledodd
, which would match any oddInt
, andeven
, which would match any evenInt
. Given this pair ofBeMatcher
s, you could check whether anInt
was odd or even with expressions like:num shouldBe odd num should not be even
For more information, see the documentation for
BeMatcher
.Checking object identity
If you need to check that two references refer to the exact same object, you can write:
ref1 should be theSameInstanceAs ref2
Checking an object's class
If you need to check that an object is an instance of a particular class or trait, you can supply the type to “
be
a
” or “be
an
”:result1 shouldBe a [Tiger] result1 should not be an [Orangutan]
Because type parameters are erased on the JVM, we recommend you insert an underscore for any type parameters when using this syntax. Both of the following test only that the result is an instance of
List[_]
, because at runtime the type parameter has been erased:result shouldBe a [List[_]] // recommended result shouldBe a [List[Fruit]] // discouraged
Checking numbers against a range
Often you may want to check whether a number is within a range. You can do that using the
+-
operator, like this:sevenDotOh should equal (6.9 +- 0.2) sevenDotOh should === (6.9 +- 0.2) sevenDotOh should be (6.9 +- 0.2) sevenDotOh shouldEqual 6.9 +- 0.2 sevenDotOh shouldBe 6.9 +- 0.2
Any of these expressions will cause a
TestFailedException
to be thrown if the floating point value,sevenDotOh
is outside the range6.7
to7.1
. You can use+-
with any typeT
for which an implicitNumeric[T]
exists, such as integral types:seven should equal (6 +- 2) seven should === (6 +- 2) seven should be (6 +- 2) seven shouldEqual 6 +- 2 seven shouldBe 6 +- 2
Checking for emptiness
You can check whether an object is "empty", like this:
traversable shouldBe empty javaMap should not be empty
The
empty
token can be used with any typeL
for which an implicitEmptiness[L]
exists. TheEmptiness
companion object provides implicits forGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
, andOption[E]
. In addition, theEmptiness
companion object provides structural implicits for types that declare anisEmpty
method that returns aBoolean
. Here are some examples:scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> List.empty shouldBe empty scala> None shouldBe empty scala> Some(1) should not be empty scala> "" shouldBe empty scala> new java.util.HashMap[Int, Int] shouldBe empty scala> new { def isEmpty = true} shouldBe empty scala> Array(1, 2, 3) should not be empty
Working with "containers"
You can check whether a collection contains a particular element like this:
traversable should contain ("five")
The
contain
syntax shown above can be used with any typeC
that has a "containing" nature, evidenced by an implicitorg.scalatest.enablers.Containing[L]
, whereL
is left-hand type on whichshould
is invoked. In theContaining
companion object, implicits are provided for typesGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
, andOption[E]
. Here are some examples:scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> List(1, 2, 3) should contain (2) scala> Map('a' -> 1, 'b' -> 2, 'c' -> 3) should contain ('b' -> 2) scala> Set(1, 2, 3) should contain (2) scala> Array(1, 2, 3) should contain (2) scala> "123" should contain ('2') scala> Some(2) should contain (2)
ScalaTest's implicit methods that provide the
Containing[L]
type classes require anEquality[E]
, whereE
is an element type. For example, to obtain aContaining[Array[Int]]
you must supply anEquality[Int]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeContaining[L]
, implicit conversions are provided in theContaining
companion object fromEquality[E]
to the various types of containers ofE
. Here's an example:scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> List("Hi", "Di", "Ho") should contain ("ho") org.scalatest.exceptions.TestFailedException: List(Hi, Di, Ho) did not contain element "ho" at ... scala> import org.scalactic.Explicitly._ import org.scalactic.Explicitly._ scala> import org.scalactic.StringNormalizations._ import org.scalactic.StringNormalizations._ scala> (List("Hi", "Di", "Ho") should contain ("ho")) (after being lowerCased)
Note that when you use the explicitly DSL with
contain
you need to wrap the entirecontain
expression in parentheses, as shown here.(List("Hi", "Di", "Ho") should contain ("ho")) (after being lowerCased) ^ ^
In addition to determining whether an object contains another object, you can use
contain
to make other determinations. For example, thecontain
oneOf
syntax ensures that one and only one of the specified elements are contained in the containing object:List(1, 2, 3, 4, 5) should contain oneOf (5, 7, 9) Some(7) should contain oneOf (5, 7, 9) "howdy" should contain oneOf ('a', 'b', 'c', 'd')
Note that if multiple specified elements appear in the containing object,
oneOf
will fail:scala> List(1, 2, 3) should contain oneOf (2, 3, 4) org.scalatest.exceptions.TestFailedException: List(1, 2, 3) did not contain one (and only one) of (2, 3, 4) at ...
If you really want to ensure one or more of the specified elements are contained in the containing object, use
atLeastOneOf
, described below, instead ofoneOf
. Keep in mind,oneOf
means "exactly one of."Note also that with any
contain
syntax, you can place custom implicitEquality[E]
instances in scope to customize how containership is determined, or use the explicitly DSL. Here's an example:(Array("Doe", "Ray", "Me") should contain oneOf ("X", "RAY", "BEAM")) (after being lowerCased)
If you have a collection of elements that you'd like to use in a "one of" comparison, you can use "oneElementOf," like this:
List(1, 2, 3, 4, 5) should contain oneElementOf List(5, 7, 9) Some(7) should contain oneElementOf Vector(5, 7, 9) "howdy" should contain oneElementOf Set('a', 'b', 'c', 'd') (Array("Doe", "Ray", "Me") should contain oneElementOf List("X", "RAY", "BEAM")) (after being lowerCased)
The
contain
noneOf
syntax does the opposite ofoneOf
: it ensures none of the specified elements are contained in the containing object:List(1, 2, 3, 4, 5) should contain noneOf (7, 8, 9) Some(0) should contain noneOf (7, 8, 9) "12345" should contain noneOf ('7', '8', '9')
If you have a collection of elements that you'd like to use in a "none of" comparison, you can use "noElementsOf," like this:
List(1, 2, 3, 4, 5) should contain noElementsOf List(7, 8, 9) Some(0) should contain noElementsOf Vector(7, 8, 9) "12345" should contain noElementsOf Set('7', '8', '9')
Working with "aggregations"
As mentioned, the "
contain
," "contain
oneOf
," and "contain
noneOf
" syntax requires aContaining[L]
be provided, whereL
is the left-hand type. Othercontain
syntax, which will be described in this section, requires anAggregating[L]
be provided, where againL
is the left-hand type. (AnAggregating[L]
instance defines the "aggregating nature" of a typeL
.) The reason, essentially, is thatcontain
syntax that makes sense forOption
is enabled byContaining[L]
, whereas syntax that does not make sense forOption
is enabled byAggregating[L]
. For example, it doesn't make sense to assert that anOption[Int]
contains all of a set of integers, as it could only ever contain one of them. But this does make sense for a type such asList[Int]
that can aggregate zero to many integers.The
Aggregating
companion object provides implicit instances ofAggregating[L]
for typesGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
. Note that these are the same types as are supported withContaining
, but withOption[E]
missing. Here are some examples:The
contain
atLeastOneOf
syntax, for example, works for any typeL
for which anAggregating[L]
exists. It ensures that at least one of (i.e., one or more of) the specified objects are contained in the containing object:List(1, 2, 3) should contain atLeastOneOf (2, 3, 4) Array(1, 2, 3) should contain atLeastOneOf (3, 4, 5) "abc" should contain atLeastOneOf ('c', 'a', 't')
Similar to
Containing[L]
, the implicit methods that provide theAggregating[L]
instances require anEquality[E]
, whereE
is an element type. For example, to obtain aAggregating[Vector[String]]
you must supply anEquality[String]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeAggregating[L]
, implicit conversions are provided in theAggregating
companion object fromEquality[E]
to the various types of aggregations ofE
. Here's an example:(Vector(" A", "B ") should contain atLeastOneOf ("a ", "b", "c")) (after being lowerCased and trimmed)
If you have a collection of elements that you'd like to use in an "at least one of" comparison, you can use "atLeastOneElementOf," like this:
List(1, 2, 3) should contain atLeastOneElementOf List(2, 3, 4) Array(1, 2, 3) should contain atLeastOneElementOf Vector(3, 4, 5) "abc" should contain atLeastOneElementOf Set('c', 'a', 't') (Vector(" A", "B ") should contain atLeastOneElementOf List("a ", "b", "c")) (after being lowerCased and trimmed)
The "
contain
atMostOneOf
" syntax lets you specify a set of objects at most one of which should be contained in the containing object:List(1, 2, 3, 4, 5) should contain atMostOneOf (5, 6, 7)
If you have a collection of elements that you'd like to use in a "at most one of" comparison, you can use "atMostOneElementOf," like this:
List(1, 2, 3, 4, 5) should contain atMostOneElementOf Vector(5, 6, 7)
The "
contain
allOf
" syntax lets you specify a set of objects that should all be contained in the containing object:List(1, 2, 3, 4, 5) should contain allOf (2, 3, 5)
If you have a collection of elements that you'd like to use in a "all of" comparison, you can use "allElementsOf," like this:
List(1, 2, 3, 4, 5) should contain allElementsOf Array(2, 3, 5)
The "
contain
only
" syntax lets you assert that the containing object contains only the specified objects, though it may contain more than one of each:List(1, 2, 3, 2, 1) should contain only (1, 2, 3)
The "
contain
theSameElementsAs
" and "contain
theSameElementsInOrderAs
syntax differ from the others in that the right hand side is aGenTraversable[_]
rather than a varargs ofAny
. (Note: in a future 2.0 milestone release, possibly 2.0.M6, these will likely be widened to accept any typeR
for which anAggregating[R]
exists.)The "
contain
theSameElementsAs
" syntax lets you assert that two aggregations contain the same objects:List(1, 2, 2, 3, 3, 3) should contain theSameElementsAs Vector(3, 2, 3, 1, 2, 3)
The number of times any family of equal objects appears must also be the same in both the left and right aggregations. The specified objects may appear multiple times, but must appear in the order they appear in the right-hand list. For example, if the last 3 element is left out of the right-hand list in the previous example, the expression would fail because the left side has three 3's and the right hand side has only two:
List(1, 2, 2, 3, 3, 3) should contain theSameElementsAs Vector(3, 2, 3, 1, 2) org.scalatest.exceptions.TestFailedException: List(1, 2, 2, 3, 3, 3) did not contain the same elements as Vector(3, 2, 3, 1, 2) at ...
Note that no
onlyElementsOf
matcher is provided, because it would have the same behavior astheSameElementsAs
. (I.e., if you were looking foronlyElementsOf
, please usetheSameElementsAs
instead.)Working with "sequences"
The rest of the
contain
syntax, which will be described in this section, requires aSequencing[L]
be provided, where againL
is the left-hand type. (ASequencing[L]
instance defines the "sequencing nature" of a typeL
.) The reason, essentially, is thatcontain
syntax that implies an "order" of elements makes sense only for types that place elements in a sequence. For example, it doesn't make sense to assert that aMap[String, Int]
orSet[Int]
contains all of a set of integers in a particular order, as these types don't necessarily define an order for their elements. But this does make sense for a type such asSeq[Int]
that does define an order for its elements.The
Sequencing
companion object provides implicit instances ofSequencing[L]
for typesGenSeq[E]
,java.util.List[E]
,String
, andArray[E]
. Here are some examples:Similar to
Containing[L]
, the implicit methods that provide theAggregating[L]
instances require anEquality[E]
, whereE
is an element type. For example, to obtain aAggregating[Vector[String]]
you must supply anEquality[String]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeAggregating[L]
, implicit conversions are provided in theAggregating
companion object fromEquality[E]
to the various types of aggregations ofE
. Here's an example:The "
contain
inOrderOnly
" syntax lets you assert that the containing object contains only the specified objects, in order. The specified objects may appear multiple times, but must appear in the order they appear in the right-hand list. Here's an example:List(1, 2, 2, 3, 3, 3) should contain inOrderOnly (1, 2, 3)
The "
contain
inOrder
" syntax lets you assert that the containing object contains only the specified objects in order, likeinOrderOnly
, but allows other objects to appear in the left-hand aggregation as well: contain more than one of each:List(0, 1, 2, 2, 99, 3, 3, 3, 5) should contain inOrder (1, 2, 3)
If you have a collection of elements that you'd like to use in a "in order" comparison, you can use "inOrderElementsOf," like this:
List(0, 1, 2, 2, 99, 3, 3, 3, 5) should contain inOrderElementsOf Array(1, 2, 3)
Note that "order" in
inOrder
,inOrderOnly
, andtheSameElementsInOrderAs
(described below) in theAggregation[L]
instances built-in to ScalaTest is defined as "iteration order".Lastly, the "
contain
theSameElementsInOrderAs
" syntax lets you assert that two aggregations contain the same exact elements in the same (iteration) order:List(1, 2, 3) should contain theSameElementsInOrderAs collection.mutable.TreeSet(3, 2, 1)
The previous assertion succeeds because the iteration order of a
TreeSet
is the natural ordering of its elements, which in this case is 1, 2, 3. An iterator obtained from the left-handList
will produce the same elements in the same order.Note that no
inOrderOnlyElementsOf
matcher is provided, because it would have the same behavior astheSameElementsInOrderAs
. (I.e., if you were looking forinOrderOnlyElementsOf
, please usetheSameElementsInOrderAs
instead.)Working with "sortables"
You can also ask whether the elements of "sortable" objects (such as
Array
s, JavaList
s, andGenSeq
s) are in sorted order, like this:List(1, 2, 3) shouldBe sorted
Working with iterators
Althought it seems desireable to provide similar matcher syntax for Scala and Java iterators to that provided for sequences like
Seq
s,Array
, andjava.util.List
, the ephemeral nature of iterators makes this problematic. Some syntax (such asshould
contain
) is relatively straightforward to support on iterators, but other syntax (such as, for example,Inspector
expressions on nested iterators) is not. Rather than allowing inconsistencies between sequences and iterators in the API, we chose to not support any such syntax directly on iterators:scala> val it = List(1, 2, 3).iterator it: Iterator[Int] = non-empty iterator scala> it should contain (2) <console>:15: error: could not find implicit value for parameter typeClass1: org.scalatest.enablers.Containing[Iterator[Int]] it should contain (2) ^
Instead, you will need to convert your iterators to a sequence explicitly before using them in matcher expressions:
scala> it.toStream should contain (2)
We recommend you convert (Scala or Java) iterators to
Stream
s, as shown in the previous example, so that you can continue to reap any potential benefits provided by the laziness of the underlying iterator.Inspector shorthands
You can use the
Inspectors
syntax with matchers as well as assertions. If you have a multi-dimensional collection, such as a list of lists, usingInspectors
is your best option:val yss = List( List(1, 2, 3), List(1, 2, 3), List(1, 2, 3) ) forAll (yss) { ys => forAll (ys) { y => y should be > 0 } }
For assertions on one-dimensional collections, however, matchers provides "inspector shorthands." Instead of writing:
val xs = List(1, 2, 3) forAll (xs) { x => x should be < 10 }
You can write:
all (xs) should be < 10
The previous statement asserts that all elements of the
xs
list should be less than 10. All of the inspectors have shorthands in matchers. Here is the full list:all
- succeeds if the assertion holds true for every elementatLeast
- succeeds if the assertion holds true for at least the specified number of elementsatMost
- succeeds if the assertion holds true for at most the specified number of elementsbetween
- succeeds if the assertion holds true for between the specified minimum and maximum number of elements, inclusiveevery
- same asall
, but lists all failing elements if it fails (whereasall
just reports the first failing element)exactly
- succeeds if the assertion holds true for exactly the specified number of elements
Here are some examples:
scala> import org.scalatest.Matchers._ import org.scalatest.Matchers._ scala> val xs = List(1, 2, 3, 4, 5) xs: List[Int] = List(1, 2, 3, 4, 5) scala> all (xs) should be > 0 scala> atMost (2, xs) should be >= 4 scala> atLeast (3, xs) should be < 5 scala> between (2, 3, xs) should (be > 1 and be < 5) scala> exactly (2, xs) should be <= 2 scala> every (xs) should be < 10 scala> // And one that fails... scala> exactly (2, xs) shouldEqual 2 org.scalatest.exceptions.TestFailedException: 'exactly(2)' inspection failed, because only 1 element satisfied the assertion block at index 1: at index 0, 1 did not equal 2, at index 2, 3 did not equal 2, at index 3, 4 did not equal 2, at index 4, 5 did not equal 2 in List(1, 2, 3, 4, 5) at ...
Like
Inspectors
, objects used with inspector shorthands can be any typeT
for which aCollecting[T, E]
is availabe, which by default includesGenTraversable
, JavaCollection
, JavaMap
,Array
s, andString
s. Here are some examples:scala> import org.scalatest._ import org.scalatest._ scala> import Matchers._ import Matchers._ scala> all (Array(1, 2, 3)) should be < 5 scala> import collection.JavaConverters._ import collection.JavaConverters._ scala> val js = List(1, 2, 3).asJava js: java.util.List[Int] = [1, 2, 3] scala> all (js) should be < 5 scala> val jmap = Map("a" -> 1, "b" -> 2).asJava jmap: java.util.Map[String,Int] = {a=1, b=2} scala> atLeast(1, jmap) shouldBe Entry("b", 2) scala> atLeast(2, "hello, world!") shouldBe 'o'
Single-element collections
To assert both that a collection contains just one "lone" element as well as something else about that element, you can use the
loneElement
syntax provided by traitLoneElement
. For example, if aSet[Int]
should contain just one element, anInt
less than or equal to 10, you could write:import LoneElement._ set.loneElement should be <= 10
You can invoke
loneElement
on any typeT
for which an implicitCollecting[E, T]
is available, whereE
is the element type returned by theloneElement
invocation. By default, you can useloneElement
onGenTraversable
, JavaCollection
, JavaMap
,Array
, andString
.Java collections and maps
You can use similar syntax on Java collections (
java.util.Collection
) and maps (java.util.Map
). For example, you can check whether a JavaCollection
orMap
isempty
, like this:javaCollection should be ('empty) javaMap should be ('empty)
Even though Java's
List
type doesn't actually have alength
orgetLength
method, you can nevertheless check the length of a JavaList
(java.util.List
) like this:javaList should have length 9
You can check the size of any Java
Collection
orMap
, like this:javaMap should have size 20 javaSet should have size 90
In addition, you can check whether a Java
Collection
contains a particular element, like this:javaCollection should contain ("five")
One difference to note between the syntax supported on Java and Scala collections is that in Java,
Map
is not a subtype ofCollection
, and does not actually define an element type. You can ask a JavaMap
for an "entry set" via theentrySet
method, which will return theMap
's key/value pairs wrapped in a set ofjava.util.Map.Entry
, but aMap
is not actually a collection ofEntry
. To make JavaMap
s easier to work with, however, ScalaTest matchers allows you to treat a JavaMap
as a collection ofEntry
, and defines a convenience implementation ofjava.util.Map.Entry
inorg.scalatest.Entry
. Here's how you use it:javaMap should contain (Entry(2, 3)) javaMap should contain oneOf (Entry(2, 3), Entry(3, 4))
You can you alse just check whether a Java
Map
contains a particular key, or value, like this:javaMap should contain key 1 javaMap should contain value "Howdy"
String
s andArray
s as collectionsYou can also use all the syntax described above for Scala and Java collections on
Array
s andString
s. Here are some examples:scala> import org.scalatest._ import org.scalatest._ scala> import Matchers._ import Matchers._ scala> atLeast (2, Array(1, 2, 3)) should be > 1 scala> atMost (2, "halloo") shouldBe 'o' scala> Array(1, 2, 3) shouldBe sorted scala> "abcdefg" shouldBe sorted scala> Array(1, 2, 3) should contain atMostOneOf (3, 4, 5) scala> "abc" should contain atMostOneOf ('c', 'd', 'e')
be
as an equality comparisonAll uses of
be
other than those shown previously perform an equality comparison. They work the same asequal
when it is used with default equality. This redundancy betweenbe
andequals
exists in part because it enables syntax that sometimes sounds more natural. For example, instead of writing:result should equal (null)
You can write:
result should be (null)
(Hopefully you won't write that too much given
null
is error prone, andOption
is usually a better, well, option.) As mentioned previously, the other difference betweenequal
andbe
is thatequal
delegates the equality check to anEquality
typeclass, whereasbe
always uses default equality. Here are some other examples ofbe
used for equality comparison:sum should be (7.0) boring should be (false) fun should be (true) list should be (Nil) option should be (None) option should be (Some(1))
As with
equal
used with default equality, usingbe
on arrays results indeep
being called on both arrays prior to callingequal
. As a result, the following expression would not throw aTestFailedException
:Array(1, 2) should be (Array(1, 2)) // succeeds (i.e., does not throw TestFailedException)
Because
be
is used in several ways in ScalaTest matcher syntax, just as it is used in many ways in English, one potential point of confusion in the event of a failure is determining whetherbe
was being used as an equality comparison or in some other way, such as a property assertion. To make it more obvious whenbe
is being used for equality, the failure messages generated for those equality checks will include the wordequal
in them. For example, if this expression fails with aTestFailedException
:option should be (Some(1))
The detail message in that
TestFailedException
will include the words"equal to"
to signifybe
was in this case being used for equality comparison:Some(2) was not equal to Some(1)
Being negative
If you wish to check the opposite of some condition, you can simply insert
not
in the expression. Here are a few examples:result should not be (null) sum should not be <= (10) mylist should not equal (yourList) string should not startWith ("Hello")
Checking that a snippet of code does not compile
Often when creating libraries you may wish to ensure that certain arrangements of code that represent potential “user errors” do not compile, so that your library is more error resistant. ScalaTest
Matchers
trait includes the following syntax for that purpose:"val a: String = 1" shouldNot compile
If you want to ensure that a snippet of code does not compile because of a type error (as opposed to a syntax error), use:
"val a: String = 1" shouldNot typeCheck
Note that the
shouldNot
typeCheck
syntax will only succeed if the given snippet of code does not compile because of a type error. A syntax error will still result on a thrownTestFailedException
.If you want to state that a snippet of code does compile, you can make that more obvious with:
"val a: Int = 1" should compile
Although the previous three constructs are implemented with macros that determine at compile time whether the snippet of code represented by the string does or does not compile, errors are reported as test failures at runtime.
Logical expressions with
and
andor
You can also combine matcher expressions with
and
and/oror
, however, you must place parentheses or curly braces around theand
oror
expression. For example, thisand
-expression would not compile, because the parentheses are missing:map should contain key ("two") and not contain value (7) // ERROR, parentheses missing!
Instead, you need to write:
map should (contain key ("two") and not contain value (7))
Here are some more examples:
number should (be > (0) and be <= (10)) option should (equal (Some(List(1, 2, 3))) or be (None)) string should ( equal ("fee") or equal ("fie") or equal ("foe") or equal ("fum") )
Two differences exist between expressions composed of these
and
andor
operators and the expressions you can write on regularBoolean
s using its&&
and||
operators. First, expressions withand
andor
do not short-circuit. The following contrived expression, for example, would print"hello, world!"
:"yellow" should (equal ("blue") and equal { println("hello, world!"); "green" })
In other words, the entire
and
oror
expression is always evaluated, so you'll see any side effects of the right-hand side even if evaluating only the left-hand side is enough to determine the ultimate result of the larger expression. Failure messages produced by these expressions will "short-circuit," however, mentioning only the left-hand side if that's enough to determine the result of the entire expression. This "short-circuiting" behavior of failure messages is intended to make it easier and quicker for you to ascertain which part of the expression caused the failure. The failure message for the previous expression, for example, would be:"yellow" did not equal "blue"
Most likely this lack of short-circuiting would rarely be noticeable, because evaluating the right hand side will usually not involve a side effect. One situation where it might show up, however, is if you attempt to
and
anull
check on a variable with an expression that uses the variable, like this:map should (not be (null) and contain key ("ouch"))
If
map
isnull
, the test will indeed fail, but with aNullArgumentException
, not aTestFailedException
. Here, theNullArgumentException
is the visible right-hand side effect. To get aTestFailedException
, you would need to check each assertion separately:map should not be (null) map should contain key ("ouch")
If
map
isnull
in this case, thenull
check in the first expression will fail with aTestFailedException
, and the second expression will never be executed.The other difference with
Boolean
operators is that although&&
has a higher precedence than||
,and
andor
have the same precedence. Thus although theBoolean
expression(a || b && c)
will evaluate the&&
expression before the||
expression, like(a || (b && c))
, the following expression:traversable should (contain (7) or contain (8) and have size (9))
Will evaluate left to right, as:
traversable should ((contain (7) or contain (8)) and have size (9))
If you really want the
and
part to be evaluated first, you'll need to put in parentheses, like this:traversable should (contain (7) or (contain (8) and have size (9)))
Working with
Option
sYou can work with options using ScalaTest's equality,
empty
,defined
, andcontain
syntax. For example, if you wish to check whether an option isNone
, you can write any of:option shouldEqual None option shouldBe None option should === (None) option shouldBe empty
If you wish to check an option is defined, and holds a specific value, you can write any of:
option shouldEqual Some("hi") option shouldBe Some("hi") option should === (Some("hi"))
If you only wish to check that an option is defined, but don't care what it's value is, you can write:
option shouldBe defined
If you mix in (or import the members of)
OptionValues
, you can write one statement that indicates you believe an option should be defined and then say something else about its value. Here's an example:import org.scalatest.OptionValues._ option.value should be < 7
As mentioned previously, you can use also use ScalaTest's
contain
,contain oneOf
, andcontain noneOf
syntax with options:Some(2) should contain (2) Some(7) should contain oneOf (5, 7, 9) Some(0) should contain noneOf (7, 8, 9)
Checking arbitrary properties with
have
Using
have
, you can check properties of any type, where a property is an attribute of any object that can be retrieved either by a public field, method, or JavaBean-styleget
oris
method, like this:book should have ( 'title ("Programming in Scala"), 'author (List("Odersky", "Spoon", "Venners")), 'pubYear (2008) )
This expression will use reflection to ensure the
title
,author
, andpubYear
properties of objectbook
are equal to the specified values. For example, it will ensure thatbook
has either a public Java field or method namedtitle
, or a public method namedgetTitle
, that when invoked (or accessed in the field case) results in a the string"Programming in Scala"
. If all specified properties exist and have their expected values, respectively, execution will continue. If one or more of the properties either does not exist, or exists but results in an unexpected value, aTestFailedException
will be thrown that explains the problem. (For the details on how a field or method is selected during this process, see the documentation forHavePropertyMatcherGenerator
.)When you use this syntax, you must place one or more property values in parentheses after
have
, seperated by commas, where a property value is a symbol indicating the name of the property followed by the expected value in parentheses. The only exceptions to this rule is the syntax for checking size and length shown previously, which does not require parentheses. If you forget and put parentheses in, however, everything will still work as you'd expect. Thus instead of writing:array should have length (3) set should have size (90)
You can alternatively, write:
array should have (length (3)) set should have (size (90))
If a property has a value different from the specified expected value, a
TestFailedError
will be thrown with a detailed message that explains the problem. For example, if you assert the following on abook
whose title isMoby Dick
:book should have ('title ("A Tale of Two Cities"))
You'll get a
TestFailedException
with this detail message:The title property had value "Moby Dick", instead of its expected value "A Tale of Two Cities", on object Book("Moby Dick", "Melville", 1851)
If you prefer to check properties in a type-safe manner, you can use a
HavePropertyMatcher
. This would allow you to write expressions such as:book should have ( title ("Programming in Scala"), author (List("Odersky", "Spoon", "Venners")), pubYear (2008) )
These expressions would fail to compile if
should
is used on an inappropriate type, as determined by the type parameter of theHavePropertyMatcher
being used. (For example,title
in this example might be of typeHavePropertyMatcher[org.publiclibrary.Book]
. If used with an appropriate type, such an expression will compile and at run time the property method or field will be accessed directly; i.e., no reflection will be used. See the documentation forHavePropertyMatcher
for more information.Using
length
andsize
withHavePropertyMatcher
sIf you want to use
length
orsize
syntax with your own customHavePropertyMatcher
s, you can do so, but you must write(of [“the type”])
afterwords. For example, you could write:book should have ( title ("A Tale of Two Cities"), length (220) (of [Book]), author ("Dickens") )
Prior to ScalaTest 2.0, “
length
(22)
” yielded aHavePropertyMatcher[Any, Int]
that used reflection to dynamically look for alength
field orgetLength
method. In ScalaTest 2.0, “length
(22)
” yields aMatcherFactory1[Any, Length]
, so it is no longer aHavePropertyMatcher
. The(of [<type>])
syntax converts the theMatcherFactory1[Any, Length]
to aHavePropertyMatcher[<type>, Int]
.Checking that an expression matches a pattern
ScalaTest's
Inside
trait allows you to make assertions after a pattern match. Here's an example:case class Name(first: String, middle: String, last: String) val name = Name("Jane", "Q", "Programmer") inside(name) { case Name(first, _, _) => first should startWith ("S") }
You can use
inside
to just ensure a pattern is matched, without making any further assertions, but a better alternative for that kind of assertion ismatchPattern
. ThematchPattern
syntax allows you to express that you expect a value to match a particular pattern, no more and no less:name should matchPattern { case Name("Sarah", _, _) => }
Using custom matchers
If none of the built-in matcher syntax (or options shown so far for extending the syntax) satisfy a particular need you have, you can create custom
Matcher
s that allow you to place your own syntax directly aftershould
. For example, classjava.io.File
has a methodisHidden
, which indicates whether a file of a certain path and name is hidden. Because theisHidden
method takes no parameters and returnsBoolean
, you can call it usingbe
with a symbol orBePropertyMatcher
, yielding assertions like:file should be ('hidden) // using a symbol file should be (hidden) // using a BePropertyMatcher
If it doesn't make sense to have your custom syntax follow
be
, you might want to create a customMatcher
instead, so your syntax can followshould
directly. For example, you might want to be able to check whether ajava.io.File
's name ends with a particular extension, like this:// using a plain-old Matcher file should endWithExtension ("txt")
ScalaTest provides several mechanism to make it easy to create custom matchers, including ways to compose new matchers out of existing ones complete with new error messages. For more information about how to create custom
Matcher
s, please see the documentation for theMatcher
trait.Checking for expected 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. With
Matchers
mixed in, you can check for an expected exception like this:an [IndexOutOfBoundsException] should be thrownBy s.charAt(-1)
If
charAt
throws an instance ofStringIndexOutOfBoundsException
, this expression will result in that exception. But ifcharAt
completes normally, or throws a different exception, this expression will complete abruptly with aTestFailedException
.If you need to further isnpect an expected exception, you can capture it using this syntax:
val thrown = the [IndexOutOfBoundsException] thrownBy s.charAt(-1)
This expression 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:
thrown.getMessage should equal ("String index out of range: -1")
If you prefer you can also capture and inspect an expected exception in one statement, like this:
the [ArithmeticException] thrownBy 1 / 0 should have message "/ by zero" the [IndexOutOfBoundsException] thrownBy { s.charAt(-1) } should have message "String index out of range: -1"
You can also state that no exception should be thrown by some code, like this:
noException should be thrownBy 0 / 1
Those pesky parens
Perhaps the most tricky part of writing assertions using ScalaTest matchers is remembering when you need or don't need parentheses, but bearing in mind a few simple rules should help. It is also reassuring to know that if you ever leave off a set of parentheses when they are required, your code will not compile. Thus the compiler will help you remember when you need the parens. That said, the rules are:
1. Although you don't always need them, you may choose to always put parentheses around right-hand values, such as the
7
innum should equal (7)
:result should equal (4) array should have length (3) book should have ( 'title ("Programming in Scala"), 'author (List("Odersky", "Spoon", "Venners")), 'pubYear (2008) ) option should be ('defined) catMap should (contain key (9) and contain value ("lives")) keyEvent should be an ('actionKey) javaSet should have size (90)
2. Except for
length
,size
andmessage
, you must always put parentheses around the list of one or more property values following ahave
:file should (exist and have ('name ("temp.txt"))) book should have ( title ("Programming in Scala"), author (List("Odersky", "Spoon", "Venners")), pubYear (2008) ) javaList should have length (9) // parens optional for length and size
3. You must always put parentheses around
and
andor
expressions, as in:catMap should (contain key (9) and contain value ("lives")) number should (equal (2) or equal (4) or equal (8))
4. Although you don't always need them, you may choose to always put parentheses around custom
Matcher
s when they appear directly afternot
:file should exist file should not (exist) file should (exist and have ('name ("temp.txt"))) file should (not (exist) and have ('name ("temp.txt")) file should (have ('name ("temp.txt") or exist) file should (have ('name ("temp.txt") or not (exist))
That's it. With a bit of practice it should become natural to you, and the compiler will always be there to tell you if you forget a set of needed parentheses.
Note: ScalaTest's matchers are in part inspired by the matchers of RSpec, Hamcrest, and specs2, and its “
shouldNot compile
” syntax by theillTyped
macro of shapeless. -
trait
MustMatchers
extends Assertions with Tolerance with MustVerb with MatcherWords with Explicitly
Trait that provides a domain specific language (DSL) for expressing assertions in tests using the word
must
.Trait that provides a domain specific language (DSL) for expressing assertions in tests using the word
must
.For example, if you mix
Matchers
into a suite class, you can write an equality assertion in that suite like this:result must equal (3)
Here
result
is a variable, and can be of any type. If the object is anInt
with the value 3, execution will continue (i.e., the expression will result in the unit value,()
). Otherwise, aTestFailedException
will be thrown with a detail message that explains the problem, such as"7 did not equal 3"
. ThisTestFailedException
will cause the test to fail.Here is a table of contents for this documentation:
- Matchers migration in ScalaTest 2.0
- Checking equality with matchers
- Checking size and length
- Checking strings
- Greater and less than
- Checking
Boolean
properties withbe
- Using custom
BeMatchers
- Checking object identity
- Checking an object's class
- Checking numbers against a range
- Checking for emptiness
- Working with "containers"
- Working with "aggregations"
- Working with "sequences"
- Working with "sortables"
- Working with iterators
- Inspector shorthands
- Single-element collections
- Java collections and maps
String
s andArray
s as collections- Be as an equality comparison
- Being negative
- Checking that a snippet of code does not compile
- Logical expressions with
and
andor
- Working with
Option
s - Checking arbitrary properties with
have
- Using
length
andsize
withHavePropertyMatcher
s - Checking that an expression matches a pattern
- Using custom matchers
- Checking for expected exceptions
- Those pesky parens
Trait
MustMatchers
is an alternative toMatchers
that provides the exact same meaning, syntax, and behavior asMatchers
, but uses the verbmust
instead ofshould
. The two traits differ only in the English semantics of the verb:should
is informal, making the code feel like conversation between the writer and the reader;must
is more formal, making the code feel more like a written specification.Checking equality with matchers
ScalaTest matchers provides five different ways to check equality, each designed to address a different need. They are:
result must equal (3) // can customize equality result must === (3) // can customize equality and enforce type constraints result must be (3) // cannot customize equality, so fastest to compile result mustEqual 3 // can customize equality, no parentheses required result mustBe 3 // cannot customize equality, so fastest to compile, no parentheses required
The “
left
must
equal
(right)
” syntax requires anorg.scalactic.Equality[L]
to be provided (either implicitly or explicitly), whereL
is the left-hand type on whichmust
is invoked. In the "left
must
equal
(right)
" case, for example,L
is the type ofleft
. Thus ifleft
is typeInt
, the "left
must
equal
(right)
" statement would require anEquality[Int]
.By default, an implicit
Equality[T]
instance is available for any typeT
, in which equality is implemented by simply invoking==
on theleft
value, passing in theright
value, with special treatment for arrays. If eitherleft
orright
is an array,deep
will be invoked on it before comparing with ==. Thus, the following expression will yield false, becauseArray
'sequals
method compares object identity:Array(1, 2) == Array(1, 2) // yields false
The next expression will by default not result in a
TestFailedException
, because defaultEquality[Array[Int]]
compares the two arrays structurally, taking into consideration the equality of the array's contents:Array(1, 2) must equal (Array(1, 2)) // succeeds (i.e., does not throw TestFailedException)
If you ever do want to verify that two arrays are actually the same object (have the same identity), you can use the
be theSameInstanceAs
syntax, described below.You can customize the meaning of equality for a type when using "
must
equal
," "must
===
," ormustEqual
syntax by defining implicitEquality
instances that will be used instead of defaultEquality
. You might do this to normalize types before comparing them with==
, for instance, or to avoid calling the==
method entirely, such as if you want to compareDouble
s with a tolerance. For an example, see the main documentation of traitEquality
.You can always supply implicit parameters explicitly, but in the case of implicit parameters of type
Equality[T]
, Scalactic provides a simple "explictly" DSL. For example, here's how you could explicitly supply anEquality[String]
instance that normalizes both left and right sides (which must be strings), by transforming them to lowercase:scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> import org.scalactic.Explicitly._ import org.scalactic.Explicitly._ scala> import org.scalactic.StringNormalizations._ import org.scalactic.StringNormalizations._ scala> "Hi" must equal ("hi") (after being lowerCased)
The
after
being
lowerCased
expression results in anEquality[String]
, which is then passed explicitly as the second curried parameter toequal
. For more information on the explictly DSL, see the main documentation for traitExplicitly
.The "
must
be
" andmustBe
syntax do not take anEquality[T]
and can therefore not be customized. They always use the default approach to equality described above. As a result, "must
be
" andmustBe
will likely be the fastest-compiling matcher syntax for equality comparisons, since the compiler need not search for an implicitEquality[T]
each time.The
must
===
syntax (and its complement,must
!==
) can be used to enforce type constraints at compile-time between the left and right sides of the equality comparison. Here's an example:scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> import org.scalactic.TypeCheckedTripleEquals._ import org.scalactic.TypeCheckedTripleEquals._ scala> Some(2) must === (2) <console>:17: error: types Some[Int] and Int do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Some[Int],Int] Some(2) must === (2) ^
By default, the "
Some(2)
must
===
(2)
" statement would fail at runtime. By mixing in the equality constraints provided byTypeCheckedTripleEquals
, however, the statement fails to compile. For more information and examples, see the main documentation for traitTypeCheckedTripleEquals
.Checking size and length
You can check the size or length of any type of object for which it makes sense. Here's how checking for length looks:
result must have length 3
Size is similar:
result must have size 10
The
length
syntax can be used withString
,Array
, anyscala.collection.GenSeq
, anyjava.util.List
, and any typeT
for which an implicitLength[T]
type class is available in scope. Similarly, thesize
syntax can be used withArray
, anyscala.collection.GenTraversable
, anyjava.util.Collection
, anyjava.util.Map
, and any typeT
for which an implicitSize[T]
type class is available in scope. You can enable thelength
orsize
syntax for your own arbitrary types, therefore, by definingLength
orSize
type classes for those types.In addition, the
length
syntax can be used with any object that has a field or method namedlength
or a method namedgetLength
. Similarly, thesize
syntax can be used with any object that has a field or method namedsize
or a method namedgetSize
. The type of alength
orsize
field, or return type of a method, must be eitherInt
orLong
. Any such method must take no parameters. (The Scala compiler will ensure at compile time that the object on whichmust
is being invoked has the appropriate structure.)Checking strings
You can check for whether a string starts with, ends with, or includes a substring like this:
string must startWith ("Hello") string must endWith ("world") string must include ("seven")
You can check for whether a string starts with, ends with, or includes a regular expression, like this:
string must startWith regex "Hel*o" string must endWith regex "wo.ld" string must include regex "wo.ld"
And you can check whether a string fully matches a regular expression, like this:
string must fullyMatch regex """(-)?(\d+)(\.\d*)?"""
The regular expression passed following the
regex
token can be either aString
or ascala.util.matching.Regex
.With the
startWith
,endWith
,include
, andfullyMatch
tokens can also be used with an optional specification of required groups, like this:"abbccxxx" must startWith regex ("a(b*)(c*)" withGroups ("bb", "cc")) "xxxabbcc" must endWith regex ("a(b*)(c*)" withGroups ("bb", "cc")) "xxxabbccxxx" must include regex ("a(b*)(c*)" withGroups ("bb", "cc")) "abbcc" must fullyMatch regex ("a(b*)(c*)" withGroups ("bb", "cc"))
You can check whether a string is empty with
empty
:s mustBe empty
You can also use most of ScalaTest's matcher syntax for collections on
String
by treating theString
s as collections of characters. For examples, see theString
s andArray
s as collections section below.Greater and less than
You can check whether any type for which an implicit
Ordering[T]
is available is greater than, less than, greater than or equal, or less than or equal to a value of typeT
. The syntax is:one must be < 7 one must be > 0 one must be <= 7 one must be >= 0
Checking
Boolean
properties withbe
If an object has a method that takes no parameters and returns boolean, you can check it by placing a
Symbol
(afterbe
) that specifies the name of the method (excluding an optional prefix of "is
"). A symbol literal in Scala begins with a tick mark and ends at the first non-identifier character. Thus,'traversableAgain
results in aSymbol
object at runtime, as does'completed
and'file
. Here's an example:iter mustBe 'traversableAgain
Given this code, ScalaTest will use reflection to look on the object referenced from
emptySet
for a method that takes no parameters and results inBoolean
, with either the nameempty
orisEmpty
. If found, it will invoke that method. If the method returnstrue
, execution will continue. But if it returnsfalse
, aTestFailedException
will be thrown that will contain a detail message, such as:non-empty iterator was not traversableAgain
This
be
syntax can be used with any reference (AnyRef
) type. If the object does not have an appropriately named predicate method, you'll get aTestFailedException
at runtime with a detailed message that explains the problem. (For the details on how a field or method is selected during this process, see the documentation forBeWord
.)If you think it reads better, you can optionally put
a
oran
afterbe
. For example,java.io.File
has two predicate methods,isFile
andisDirectory
. Thus with aFile
object namedtemp
, you could write:temp must be a 'file
Or, given
java.awt.event.KeyEvent
has a methodisActionKey
that takes no arguments and returnsBoolean
, you could assert that aKeyEvent
is an action key with:keyEvent must be an 'actionKey
If you prefer to check
Boolean
properties in a type-safe manner, you can use aBePropertyMatcher
. This would allow you to write expressions such as:xs mustBe traversableAgain temp must be a file keyEvent must be an actionKey
These expressions would fail to compile if
must
is used on an inappropriate type, as determined by the type parameter of theBePropertyMatcher
being used. (For example,file
in this example would likely be of typeBePropertyMatcher[java.io.File]
. If used with an appropriate type, such an expression will compile and at run time theBoolean
property method or field will be accessed directly; i.e., no reflection will be used. See the documentation forBePropertyMatcher
for more information.Using custom
BeMatchers
If you want to create a new way of using
be
, which doesn't map to an actual property on the type you care about, you can create aBeMatcher
. You could use this, for example, to createBeMatcher[Int]
calledodd
, which would match any oddInt
, andeven
, which would match any evenInt
. Given this pair ofBeMatcher
s, you could check whether anInt
was odd or even with expressions like:num mustBe odd num must not be even
For more information, see the documentation for
BeMatcher
.Checking object identity
If you need to check that two references refer to the exact same object, you can write:
ref1 must be theSameInstanceAs ref2
Checking an object's class
If you need to check that an object is an instance of a particular class or trait, you can supply the type to “
be
a
” or “be
an
”:result1 mustBe a [Tiger] result1 must not be an [Orangutan]
Because type parameters are erased on the JVM, we recommend you insert an underscore for any type parameters when using this syntax. Both of the following test only that the result is an instance of
List[_]
, because at runtime the type parameter has been erased:result mustBe a [List[_]] // recommended result mustBe a [List[Fruit]] // discouraged
Checking numbers against a range
Often you may want to check whether a number is within a range. You can do that using the
+-
operator, like this:sevenDotOh must equal (6.9 +- 0.2) sevenDotOh must === (6.9 +- 0.2) sevenDotOh must be (6.9 +- 0.2) sevenDotOh mustEqual 6.9 +- 0.2 sevenDotOh mustBe 6.9 +- 0.2
Any of these expressions will cause a
TestFailedException
to be thrown if the floating point value,sevenDotOh
is outside the range6.7
to7.1
. You can use+-
with any typeT
for which an implicitNumeric[T]
exists, such as integral types:seven must equal (6 +- 2) seven must === (6 +- 2) seven must be (6 +- 2) seven mustEqual 6 +- 2 seven mustBe 6 +- 2
Checking for emptiness
You can check whether an object is "empty", like this:
traversable mustBe empty javaMap must not be empty
The
empty
token can be used with any typeL
for which an implicitEmptiness[L]
exists. TheEmptiness
companion object provides implicits forGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
, andOption[E]
. In addition, theEmptiness
companion object provides structural implicits for types that declare anisEmpty
method that returns aBoolean
. Here are some examples:scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> List.empty mustBe empty scala> None mustBe empty scala> Some(1) must not be empty scala> "" mustBe empty scala> new java.util.HashMap[Int, Int] mustBe empty scala> new { def isEmpty = true} mustBe empty scala> Array(1, 2, 3) must not be empty
Working with "containers"
You can check whether a collection contains a particular element like this:
traversable must contain ("five")
The
contain
syntax shown above can be used with any typeC
that has a "containing" nature, evidenced by an implicitorg.scalatest.enablers.Containing[L]
, whereL
is left-hand type on whichmust
is invoked. In theContaining
companion object, implicits are provided for typesGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
, andOption[E]
. Here are some examples:scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> List(1, 2, 3) must contain (2) scala> Map('a' -> 1, 'b' -> 2, 'c' -> 3) must contain ('b' -> 2) scala> Set(1, 2, 3) must contain (2) scala> Array(1, 2, 3) must contain (2) scala> "123" must contain ('2') scala> Some(2) must contain (2)
ScalaTest's implicit methods that provide the
Containing[L]
type classes require anEquality[E]
, whereE
is an element type. For example, to obtain aContaining[Array[Int]]
you must supply anEquality[Int]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeContaining[L]
, implicit conversions are provided in theContaining
companion object fromEquality[E]
to the various types of containers ofE
. Here's an example:scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> List("Hi", "Di", "Ho") must contain ("ho") org.scalatest.exceptions.TestFailedException: List(Hi, Di, Ho) did not contain element "ho" at ... scala> import org.scalactic.Explicitly._ import org.scalactic.Explicitly._ scala> import org.scalactic.StringNormalizations._ import org.scalactic.StringNormalizations._ scala> (List("Hi", "Di", "Ho") must contain ("ho")) (after being lowerCased)
Note that when you use the explicitly DSL with
contain
you need to wrap the entirecontain
expression in parentheses, as shown here.(List("Hi", "Di", "Ho") must contain ("ho")) (after being lowerCased) ^ ^
In addition to determining whether an object contains another object, you can use
contain
to make other determinations. For example, thecontain
oneOf
syntax ensures that one and only one of the specified elements are contained in the containing object:List(1, 2, 3, 4, 5) must contain oneOf (5, 7, 9) Some(7) must contain oneOf (5, 7, 9) "howdy" must contain oneOf ('a', 'b', 'c', 'd')
Note that if multiple specified elements appear in the containing object,
oneOf
will fail:scala> List(1, 2, 3) must contain oneOf (2, 3, 4) org.scalatest.exceptions.TestFailedException: List(1, 2, 3) did not contain one (and only one) of (2, 3, 4) at ...
If you really want to ensure one or more of the specified elements are contained in the containing object, use
atLeastOneOf
, described below, instead ofoneOf
. Keep in mind,oneOf
means "exactly one of."Note also that with any
contain
syntax, you can place custom implicitEquality[E]
instances in scope to customize how containership is determined, or use the explicitly DSL. Here's an example:(Array("Doe", "Ray", "Me") must contain oneOf ("X", "RAY", "BEAM")) (after being lowerCased)
If you have a collection of elements that you'd like to use in a "one of" comparison, you can use "oneElementOf," like this:
List(1, 2, 3, 4, 5) must contain oneElementOf List(5, 7, 9) Some(7) must contain oneElementOf Vector(5, 7, 9) "howdy" must contain oneElementOf Set('a', 'b', 'c', 'd') (Array("Doe", "Ray", "Me") must contain oneElementOf List("X", "RAY", "BEAM")) (after being lowerCased)
The
contain
noneOf
syntax does the opposite ofoneOf
: it ensures none of the specified elements are contained in the containing object:List(1, 2, 3, 4, 5) must contain noneOf (7, 8, 9) Some(0) must contain noneOf (7, 8, 9) "12345" must contain noneOf ('7', '8', '9')
If you have a collection of elements that you'd like to use in a "none of" comparison, you can use "noElementsOf," like this:
List(1, 2, 3, 4, 5) must contain noElementsOf List(7, 8, 9) Some(0) must contain noElementsOf Vector(7, 8, 9) "12345" must contain noElementsOf Set('7', '8', '9')
Working with "aggregations"
As mentioned, the "
contain
," "contain
oneOf
," and "contain
noneOf
" syntax requires aContaining[L]
be provided, whereL
is the left-hand type. Othercontain
syntax, which will be described in this section, requires anAggregating[L]
be provided, where againL
is the left-hand type. (AnAggregating[L]
instance defines the "aggregating nature" of a typeL
.) The reason, essentially, is thatcontain
syntax that makes sense forOption
is enabled byContaining[L]
, whereas syntax that does not make sense forOption
is enabled byAggregating[L]
. For example, it doesn't make sense to assert that anOption[Int]
contains all of a set of integers, as it could only ever contain one of them. But this does make sense for a type such asList[Int]
that can aggregate zero to many integers.The
Aggregating
companion object provides implicit instances ofAggregating[L]
for typesGenTraversable[E]
,java.util.Collection[E]
,java.util.Map[K, V]
,String
,Array[E]
. Note that these are the same types as are supported withContaining
, but withOption[E]
missing. Here are some examples:The
contain
atLeastOneOf
syntax, for example, works for any typeL
for which anAggregating[L]
exists. It ensures that at least one of (i.e., one or more of) the specified objects are contained in the containing object:List(1, 2, 3) must contain atLeastOneOf (2, 3, 4) Array(1, 2, 3) must contain atLeastOneOf (3, 4, 5) "abc" must contain atLeastOneOf ('c', 'a', 't')
Similar to
Containing[L]
, the implicit methods that provide theAggregating[L]
instances require anEquality[E]
, whereE
is an element type. For example, to obtain aAggregating[Vector[String]]
you must supply anEquality[String]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeAggregating[L]
, implicit conversions are provided in theAggregating
companion object fromEquality[E]
to the various types of aggregations ofE
. Here's an example:(Vector(" A", "B ") must contain atLeastOneOf ("a ", "b", "c")) (after being lowerCased and trimmed)
If you have a collection of elements that you'd like to use in an "at least one of" comparison, you can use "atLeastOneElementOf," like this:
List(1, 2, 3) must contain atLeastOneElementOf List(2, 3, 4) Array(1, 2, 3) must contain atLeastOneElementOf Vector(3, 4, 5) "abc" must contain atLeastOneElementOf Set('c', 'a', 't') (Vector(" A", "B ") must contain atLeastOneElementOf List("a ", "b", "c")) (after being lowerCased and trimmed)
The "
contain
atMostOneOf
" syntax lets you specify a set of objects at most one of which must be contained in the containing object:List(1, 2, 3, 4, 5) must contain atMostOneOf (5, 6, 7)
If you have a collection of elements that you'd like to use in a "at most one of" comparison, you can use "atMostOneElementOf," like this:
List(1, 2, 3, 4, 5) must contain atMostOneElementOf Vector(5, 6, 7)
The "
contain
allOf
" syntax lets you specify a set of objects that must all be contained in the containing object:List(1, 2, 3, 4, 5) must contain allOf (2, 3, 5)
If you have a collection of elements that you'd like to use in a "all of" comparison, you can use "allElementsOf," like this:
List(1, 2, 3, 4, 5) must contain allElementsOf Array(2, 3, 5)
The "
contain
only
" syntax lets you assert that the containing object contains only the specified objects, though it may contain more than one of each:List(1, 2, 3, 2, 1) must contain only (1, 2, 3)
The "
contain
theSameElementsAs
" and "contain
theSameElementsInOrderAs
syntax differ from the others in that the right hand side is aGenTraversable[_]
rather than a varargs ofAny
. (Note: in a future 2.0 milestone release, possibly 2.0.M6, these will likely be widened to accept any typeR
for which anAggregating[R]
exists.)The "
contain
theSameElementsAs
" syntax lets you assert that two aggregations contain the same objects:List(1, 2, 2, 3, 3, 3) must contain theSameElementsAs Vector(3, 2, 3, 1, 2, 3)
The number of times any family of equal objects appears must also be the same in both the left and right aggregations. The specified objects may appear multiple times, but must appear in the order they appear in the right-hand list. For example, if the last 3 element is left out of the right-hand list in the previous example, the expression would fail because the left side has three 3's and the right hand side has only two:
List(1, 2, 2, 3, 3, 3) must contain theSameElementsAs Vector(3, 2, 3, 1, 2) org.scalatest.exceptions.TestFailedException: List(1, 2, 2, 3, 3, 3) did not contain the same elements as Vector(3, 2, 3, 1, 2) at ...
Note that no
onlyElementsOf
matcher is provided, because it would have the same behavior astheSameElementsAs
. (I.e., if you were looking foronlyElementsOf
, please usetheSameElementsAs
instead.)Working with "sequences"
The rest of the
contain
syntax, which will be described in this section, requires aSequencing[L]
be provided, where againL
is the left-hand type. (ASequencing[L]
instance defines the "sequencing nature" of a typeL
.) The reason, essentially, is thatcontain
syntax that implies an "order" of elements makes sense only for types that place elements in a sequence. For example, it doesn't make sense to assert that aMap[String, Int]
orSet[Int]
contains all of a set of integers in a particular order, as these types don't necessarily define an order for their elements. But this does make sense for a type such asSeq[Int]
that does define an order for its elements.The
Sequencing
companion object provides implicit instances ofSequencing[L]
for typesGenSeq[E]
,java.util.List[E]
,String
, andArray[E]
. Here are some examples:Similar to
Containing[L]
, the implicit methods that provide theAggregating[L]
instances require anEquality[E]
, whereE
is an element type. For example, to obtain aAggregating[Vector[String]]
you must supply anEquality[String]
, either implicitly or explicitly. Thecontain
syntax uses thisEquality[E]
to determine containership. Thus if you want to change how containership is determined for an element typeE
, place an implicitEquality[E]
in scope or use the explicitly DSL. Although the implicit parameter required for thecontain
syntax is of typeAggregating[L]
, implicit conversions are provided in theAggregating
companion object fromEquality[E]
to the various types of aggregations ofE
. Here's an example:The "
contain
inOrderOnly
" syntax lets you assert that the containing object contains only the specified objects, in order. The specified objects may appear multiple times, but must appear in the order they appear in the right-hand list. Here's an example:List(1, 2, 2, 3, 3, 3) must contain inOrderOnly (1, 2, 3)
The "
contain
inOrder
" syntax lets you assert that the containing object contains only the specified objects in order, likeinOrderOnly
, but allows other objects to appear in the left-hand aggregation as well: contain more than one of each:List(0, 1, 2, 2, 99, 3, 3, 3, 5) must contain inOrder (1, 2, 3)
If you have a collection of elements that you'd like to use in a "in order" comparison, you can use "inOrderElementsOf," like this:
List(0, 1, 2, 2, 99, 3, 3, 3, 5) must contain inOrderElementsOf Array(1, 2, 3)
Note that "order" in
inOrder
,inOrderOnly
, andtheSameElementsInOrderAs
(described below) in theAggregation[L]
instances built-in to ScalaTest is defined as "iteration order".Lastly, the "
contain
theSameElementsInOrderAs
" syntax lets you assert that two aggregations contain the same exact elements in the same (iteration) order:List(1, 2, 3) must contain theSameElementsInOrderAs collection.mutable.TreeSet(3, 2, 1)
The previous assertion succeeds because the iteration order of a
TreeSet
is the natural ordering of its elements, which in this case is 1, 2, 3. An iterator obtained from the left-handList
will produce the same elements in the same order.Note that no
inOrderOnlyElementsOf
matcher is provided, because it would have the same behavior astheSameElementsInOrderAs
. (I.e., if you were looking forinOrderOnlyElementsOf
, please usetheSameElementsInOrderAs
instead.)Working with "sortables"
You can also ask whether the elements of "sortable" objects (such as
Array
s, JavaList
s, andGenSeq
s) are in sorted order, like this:List(1, 2, 3) mustBe sorted
Working with iterators
Althought it seems desireable to provide similar matcher syntax for Scala and Java iterators to that provided for sequences like
Seq
s,Array
, andjava.util.List
, the ephemeral nature of iterators makes this problematic. Some syntax (such asmust
contain
) is relatively straightforward to support on iterators, but other syntax (such as, for example,Inspector
expressions on nested iterators) is not. Rather than allowing inconsistencies between sequences and iterators in the API, we chose to not support any such syntax directly on iterators:scala> val it = List(1, 2, 3).iterator it: Iterator[Int] = non-empty iterator scala> it must contain (2) <console>:15: error: could not find implicit value for parameter typeClass1: org.scalatest.enablers.Containing[Iterator[Int]] it must contain (2) ^
Instead, you will need to convert your iterators to a sequence explicitly before using them in matcher expressions:
scala> it.toStream must contain (2)
We recommend you convert (Scala or Java) iterators to
Stream
s, as shown in the previous example, so that you can continue to reap any potential benefits provided by the laziness of the underlying iterator.Inspector shorthands
You can use the
Inspectors
syntax with matchers as well as assertions. If you have a multi-dimensional collection, such as a list of lists, usingInspectors
is your best option:val yss = List( List(1, 2, 3), List(1, 2, 3), List(1, 2, 3) ) forAll (yss) { ys => forAll (ys) { y => y must be > 0 } }
For assertions on one-dimensional collections, however, matchers provides "inspector shorthands." Instead of writing:
val xs = List(1, 2, 3) forAll (xs) { x => x must be < 10 }
You can write:
all (xs) must be < 10
The previous statement asserts that all elements of the
xs
list must be less than 10. All of the inspectors have shorthands in matchers. Here is the full list:all
- succeeds if the assertion holds true for every elementatLeast
- succeeds if the assertion holds true for at least the specified number of elementsatMost
- succeeds if the assertion holds true for at most the specified number of elementsbetween
- succeeds if the assertion holds true for between the specified minimum and maximum number of elements, inclusiveevery
- same asall
, but lists all failing elements if it fails (whereasall
just reports the first failing element)exactly
- succeeds if the assertion holds true for exactly the specified number of elements
Here are some examples:
scala> import org.scalatest.MustMatchers._ import org.scalatest.MustMatchers._ scala> val xs = List(1, 2, 3, 4, 5) xs: List[Int] = List(1, 2, 3, 4, 5) scala> all (xs) must be > 0 scala> atMost (2, xs) must be >= 4 scala> atLeast (3, xs) must be < 5 scala> between (2, 3, xs) must (be > 1 and be < 5) scala> exactly (2, xs) must be <= 2 scala> every (xs) must be < 10 scala> // And one that fails... scala> exactly (2, xs) mustEqual 2 org.scalatest.exceptions.TestFailedException: 'exactly(2)' inspection failed, because only 1 element satisfied the assertion block at index 1: at index 0, 1 did not equal 2, at index 2, 3 did not equal 2, at index 3, 4 did not equal 2, at index 4, 5 did not equal 2 in List(1, 2, 3, 4, 5) at ...
Like
Inspectors
, objects used with inspector shorthands can be any typeT
for which aCollecting[T, E]
is availabe, which by default includesGenTraversable
, JavaCollection
, JavaMap
,Array
s, andString
s. Here are some examples:scala> import org.scalatest._ import org.scalatest._ scala> import MustMatchers._ import MustMatchers._ scala> all (Array(1, 2, 3)) must be < 5 scala> import collection.JavaConverters._ import collection.JavaConverters._ scala> val js = List(1, 2, 3).asJava js: java.util.List[Int] = [1, 2, 3] scala> all (js) must be < 5 scala> val jmap = Map("a" -> 1, "b" -> 2).asJava jmap: java.util.Map[String,Int] = {a=1, b=2} scala> atLeast(1, jmap) mustBe Entry("b", 2) scala> atLeast(2, "hello, world!") mustBe 'o'
Single-element collections
To assert both that a collection contains just one "lone" element as well as something else about that element, you can use the
loneElement
syntax provided by traitLoneElement
. For example, if aSet[Int]
must contain just one element, anInt
less than or equal to 10, you could write:import LoneElement._ set.loneElement must be <= 10
You can invoke
loneElement
on any typeT
for which an implicitCollecting[E, T]
is available, whereE
is the element type returned by theloneElement
invocation. By default, you can useloneElement
onGenTraversable
, JavaCollection
, JavaMap
,Array
, andString
.Java collections and maps
You can use similar syntax on Java collections (
java.util.Collection
) and maps (java.util.Map
). For example, you can check whether a JavaCollection
orMap
isempty
, like this:javaCollection must be ('empty) javaMap must be ('empty)
Even though Java's
List
type doesn't actually have alength
orgetLength
method, you can nevertheless check the length of a JavaList
(java.util.List
) like this:javaList must have length 9
You can check the size of any Java
Collection
orMap
, like this:javaMap must have size 20 javaSet must have size 90
In addition, you can check whether a Java
Collection
contains a particular element, like this:javaCollection must contain ("five")
One difference to note between the syntax supported on Java and Scala collections is that in Java,
Map
is not a subtype ofCollection
, and does not actually define an element type. You can ask a JavaMap
for an "entry set" via theentrySet
method, which will return theMap
's key/value pairs wrapped in a set ofjava.util.Map.Entry
, but aMap
is not actually a collection ofEntry
. To make JavaMap
s easier to work with, however, ScalaTest matchers allows you to treat a JavaMap
as a collection ofEntry
, and defines a convenience implementation ofjava.util.Map.Entry
inorg.scalatest.Entry
. Here's how you use it:javaMap must contain (Entry(2, 3)) javaMap must contain oneOf (Entry(2, 3), Entry(3, 4))
You can you alse just check whether a Java
Map
contains a particular key, or value, like this:javaMap must contain key 1 javaMap must contain value "Howdy"
String
s andArray
s as collectionsYou can also use all the syntax described above for Scala and Java collections on
Array
s andString
s. Here are some examples:scala> import org.scalatest._ import org.scalatest._ scala> import MustMatchers._ import MustMatchers._ scala> atLeast (2, Array(1, 2, 3)) must be > 1 scala> atMost (2, "halloo") mustBe 'o' scala> Array(1, 2, 3) mustBe sorted scala> "abcdefg" mustBe sorted scala> Array(1, 2, 3) must contain atMostOneOf (3, 4, 5) scala> "abc" must contain atMostOneOf ('c', 'd', 'e')
be
as an equality comparisonAll uses of
be
other than those shown previously perform an equality comparison. They work the same asequal
when it is used with default equality. This redundancy betweenbe
andequals
exists in part because it enables syntax that sometimes sounds more natural. For example, instead of writing:result must equal (null)
You can write:
result must be (null)
(Hopefully you won't write that too much given
null
is error prone, andOption
is usually a better, well, option.) As mentioned previously, the other difference betweenequal
andbe
is thatequal
delegates the equality check to anEquality
typeclass, whereasbe
always uses default equality. Here are some other examples ofbe
used for equality comparison:sum must be (7.0) boring must be (false) fun must be (true) list must be (Nil) option must be (None) option must be (Some(1))
As with
equal
used with default equality, usingbe
on arrays results indeep
being called on both arrays prior to callingequal
. As a result, the following expression would not throw aTestFailedException
:Array(1, 2) must be (Array(1, 2)) // succeeds (i.e., does not throw TestFailedException)
Because
be
is used in several ways in ScalaTest matcher syntax, just as it is used in many ways in English, one potential point of confusion in the event of a failure is determining whetherbe
was being used as an equality comparison or in some other way, such as a property assertion. To make it more obvious whenbe
is being used for equality, the failure messages generated for those equality checks will include the wordequal
in them. For example, if this expression fails with aTestFailedException
:option must be (Some(1))
The detail message in that
TestFailedException
will include the words"equal to"
to signifybe
was in this case being used for equality comparison:Some(2) was not equal to Some(1)
Being negative
If you wish to check the opposite of some condition, you can simply insert
not
in the expression. Here are a few examples:result must not be (null) sum must not be <= (10) mylist must not equal (yourList) string must not startWith ("Hello")
Checking that a snippet of code does not compile
Often when creating libraries you may wish to ensure that certain arrangements of code that represent potential “user errors” do not compile, so that your library is more error resistant. ScalaTest
Matchers
trait includes the following syntax for that purpose:"val a: String = 1" mustNot compile
If you want to ensure that a snippet of code does not compile because of a type error (as opposed to a syntax error), use:
"val a: String = 1" mustNot typeCheck
Note that the
mustNot
typeCheck
syntax will only succeed if the given snippet of code does not compile because of a type error. A syntax error will still result on a thrownTestFailedException
.If you want to state that a snippet of code does compile, you can make that more obvious with:
"val a: Int = 1" must compile
Although the previous three constructs are implemented with macros that determine at compile time whether the snippet of code represented by the string does or does not compile, errors are reported as test failures at runtime.
Logical expressions with
and
andor
You can also combine matcher expressions with
and
and/oror
, however, you must place parentheses or curly braces around theand
oror
expression. For example, thisand
-expression would not compile, because the parentheses are missing:map must contain key ("two") and not contain value (7) // ERROR, parentheses missing!
Instead, you need to write:
map must (contain key ("two") and not contain value (7))
Here are some more examples:
number must (be > (0) and be <= (10)) option must (equal (Some(List(1, 2, 3))) or be (None)) string must ( equal ("fee") or equal ("fie") or equal ("foe") or equal ("fum") )
Two differences exist between expressions composed of these
and
andor
operators and the expressions you can write on regularBoolean
s using its&&
and||
operators. First, expressions withand
andor
do not short-circuit. The following contrived expression, for example, would print"hello, world!"
:"yellow" must (equal ("blue") and equal { println("hello, world!"); "green" })
In other words, the entire
and
oror
expression is always evaluated, so you'll see any side effects of the right-hand side even if evaluating only the left-hand side is enough to determine the ultimate result of the larger expression. Failure messages produced by these expressions will "short-circuit," however, mentioning only the left-hand side if that's enough to determine the result of the entire expression. This "short-circuiting" behavior of failure messages is intended to make it easier and quicker for you to ascertain which part of the expression caused the failure. The failure message for the previous expression, for example, would be:"yellow" did not equal "blue"
Most likely this lack of short-circuiting would rarely be noticeable, because evaluating the right hand side will usually not involve a side effect. One situation where it might show up, however, is if you attempt to
and
anull
check on a variable with an expression that uses the variable, like this:map must (not be (null) and contain key ("ouch"))
If
map
isnull
, the test will indeed fail, but with aNullArgumentException
, not aTestFailedException
. Here, theNullArgumentException
is the visible right-hand side effect. To get aTestFailedException
, you would need to check each assertion separately:map must not be (null) map must contain key ("ouch")
If
map
isnull
in this case, thenull
check in the first expression will fail with aTestFailedException
, and the second expression will never be executed.The other difference with
Boolean
operators is that although&&
has a higher precedence than||
,and
andor
have the same precedence. Thus although theBoolean
expression(a || b && c)
will evaluate the&&
expression before the||
expression, like(a || (b && c))
, the following expression:traversable must (contain (7) or contain (8) and have size (9))
Will evaluate left to right, as:
traversable must ((contain (7) or contain (8)) and have size (9))
If you really want the
and
part to be evaluated first, you'll need to put in parentheses, like this:traversable must (contain (7) or (contain (8) and have size (9)))
Working with
Option
sYou can work with options using ScalaTest's equality,
empty
,defined
, andcontain
syntax. For example, if you wish to check whether an option isNone
, you can write any of:option mustEqual None option mustBe None option must === (None) option mustBe empty
If you wish to check an option is defined, and holds a specific value, you can write any of:
option mustEqual Some("hi") option mustBe Some("hi") option must === (Some("hi"))
If you only wish to check that an option is defined, but don't care what it's value is, you can write:
option mustBe defined
If you mix in (or import the members of)
OptionValues
, you can write one statement that indicates you believe an option must be defined and then say something else about its value. Here's an example:import org.scalatest.OptionValues._ option.value must be < 7
As mentioned previously, you can use also use ScalaTest's
contain
,contain oneOf
, andcontain noneOf
syntax with options:Some(2) must contain (2) Some(7) must contain oneOf (5, 7, 9) Some(0) must contain noneOf (7, 8, 9)
Checking arbitrary properties with
have
Using
have
, you can check properties of any type, where a property is an attribute of any object that can be retrieved either by a public field, method, or JavaBean-styleget
oris
method, like this:book must have ( 'title ("Programming in Scala"), 'author (List("Odersky", "Spoon", "Venners")), 'pubYear (2008) )
This expression will use reflection to ensure the
title
,author
, andpubYear
properties of objectbook
are equal to the specified values. For example, it will ensure thatbook
has either a public Java field or method namedtitle
, or a public method namedgetTitle
, that when invoked (or accessed in the field case) results in a the string"Programming in Scala"
. If all specified properties exist and have their expected values, respectively, execution will continue. If one or more of the properties either does not exist, or exists but results in an unexpected value, aTestFailedException
will be thrown that explains the problem. (For the details on how a field or method is selected during this process, see the documentation forHavePropertyMatcherGenerator
.)When you use this syntax, you must place one or more property values in parentheses after
have
, seperated by commas, where a property value is a symbol indicating the name of the property followed by the expected value in parentheses. The only exceptions to this rule is the syntax for checking size and length shown previously, which does not require parentheses. If you forget and put parentheses in, however, everything will still work as you'd expect. Thus instead of writing:array must have length (3) set must have size (90)
You can alternatively, write:
array must have (length (3)) set must have (size (90))
If a property has a value different from the specified expected value, a
TestFailedError
will be thrown with a detailed message that explains the problem. For example, if you assert the following on abook
whose title isMoby Dick
:book must have ('title ("A Tale of Two Cities"))
You'll get a
TestFailedException
with this detail message:The title property had value "Moby Dick", instead of its expected value "A Tale of Two Cities", on object Book("Moby Dick", "Melville", 1851)
If you prefer to check properties in a type-safe manner, you can use a
HavePropertyMatcher
. This would allow you to write expressions such as:book must have ( title ("Programming in Scala"), author (List("Odersky", "Spoon", "Venners")), pubYear (2008) )
These expressions would fail to compile if
must
is used on an inappropriate type, as determined by the type parameter of theHavePropertyMatcher
being used. (For example,title
in this example might be of typeHavePropertyMatcher[org.publiclibrary.Book]
. If used with an appropriate type, such an expression will compile and at run time the property method or field will be accessed directly; i.e., no reflection will be used. See the documentation forHavePropertyMatcher
for more information.Using
length
andsize
withHavePropertyMatcher
sIf you want to use
length
orsize
syntax with your own customHavePropertyMatcher
s, you can do so, but you must write(of [“the type”])
afterwords. For example, you could write:book must have ( title ("A Tale of Two Cities"), length (220) (of [Book]), author ("Dickens") )
Prior to ScalaTest 2.0, “
length
(22)
” yielded aHavePropertyMatcher[Any, Int]
that used reflection to dynamically look for alength
field orgetLength
method. In ScalaTest 2.0, “length
(22)
” yields aMatcherFactory1[Any, Length]
, so it is no longer aHavePropertyMatcher
. The(of [<type>])
syntax converts the theMatcherFactory1[Any, Length]
to aHavePropertyMatcher[<type>, Int]
.Checking that an expression matches a pattern
ScalaTest's
Inside
trait allows you to make assertions after a pattern match. Here's an example:case class Name(first: String, middle: String, last: String) val name = Name("Jane", "Q", "Programmer") inside(name) { case Name(first, _, _) => first must startWith ("S") }
You can use
inside
to just ensure a pattern is matched, without making any further assertions, but a better alternative for that kind of assertion ismatchPattern
. ThematchPattern
syntax allows you to express that you expect a value to match a particular pattern, no more and no less:name must matchPattern { case Name("Sarah", _, _) => }
Using custom matchers
If none of the built-in matcher syntax (or options shown so far for extending the syntax) satisfy a particular need you have, you can create custom
Matcher
s that allow you to place your own syntax directly aftermust
. For example, classjava.io.File
has a methodisHidden
, which indicates whether a file of a certain path and name is hidden. Because theisHidden
method takes no parameters and returnsBoolean
, you can call it usingbe
with a symbol orBePropertyMatcher
, yielding assertions like:file must be ('hidden) // using a symbol file must be (hidden) // using a BePropertyMatcher
If it doesn't make sense to have your custom syntax follow
be
, you might want to create a customMatcher
instead, so your syntax can followmust
directly. For example, you might want to be able to check whether ajava.io.File
's name ends with a particular extension, like this:// using a plain-old Matcher file must endWithExtension ("txt")
ScalaTest provides several mechanism to make it easy to create custom matchers, including ways to compose new matchers out of existing ones complete with new error messages. For more information about how to create custom
Matcher
s, please see the documentation for theMatcher
trait.Checking for expected 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. With
Matchers
mixed in, you can check for an expected exception like this:an [IndexOutOfBoundsException] must be thrownBy s.charAt(-1)
If
charAt
throws an instance ofStringIndexOutOfBoundsException
, this expression will result in that exception. But ifcharAt
completes normally, or throws a different exception, this expression will complete abruptly with aTestFailedException
.If you need to further isnpect an expected exception, you can capture it using this syntax:
val thrown = the [IndexOutOfBoundsException] thrownBy s.charAt(-1)
This expression 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:
thrown.getMessage must equal ("String index out of range: -1")
If you prefer you can also capture and inspect an expected exception in one statement, like this:
the [ArithmeticException] thrownBy 1 / 0 must have message "/ by zero" the [IndexOutOfBoundsException] thrownBy { s.charAt(-1) } must have message "String index out of range: -1"
You can also state that no exception must be thrown by some code, like this:
noException must be thrownBy 0 / 1
Those pesky parens
Perhaps the most tricky part of writing assertions using ScalaTest matchers is remembering when you need or don't need parentheses, but bearing in mind a few simple rules should help. It is also reassuring to know that if you ever leave off a set of parentheses when they are required, your code will not compile. Thus the compiler will help you remember when you need the parens. That said, the rules are:
1. Although you don't always need them, you may choose to always put parentheses around right-hand values, such as the
7
innum must equal (7)
:result must equal (4) array must have length (3) book must have ( 'title ("Programming in Scala"), 'author (List("Odersky", "Spoon", "Venners")), 'pubYear (2008) ) option must be ('defined) catMap must (contain key (9) and contain value ("lives")) keyEvent must be an ('actionKey) javaSet must have size (90)
2. Except for
length
,size
andmessage
, you must always put parentheses around the list of one or more property values following ahave
:file must (exist and have ('name ("temp.txt"))) book must have ( title ("Programming in Scala"), author (List("Odersky", "Spoon", "Venners")), pubYear (2008) ) javaList must have length (9) // parens optional for length and size
3. You must always put parentheses around
and
andor
expressions, as in:catMap must (contain key (9) and contain value ("lives")) number must (equal (2) or equal (4) or equal (8))
4. Although you don't always need them, you may choose to always put parentheses around custom
Matcher
s when they appear directly afternot
:file must exist file must not (exist) file must (exist and have ('name ("temp.txt"))) file must (not (exist) and have ('name ("temp.txt")) file must (have ('name ("temp.txt") or exist) file must (have ('name ("temp.txt") or not (exist))
That's it. With a bit of practice it should become natural to you, and the compiler will always be there to tell you if you forget a set of needed parentheses.
Note: ScalaTest's matchers are in part inspired by the matchers of RSpec, Hamcrest, and specs2, and its “
mustNot compile
” syntax by theillTyped
macro of shapeless. -
trait
NonImplicitAssertions
extends Assertions
Trait that can be mixed into a
Suite
to disable the implicit conversions provided by default in traitAssertions
, which traitSuite
extends.Trait that can be mixed into a
Suite
to disable the implicit conversions provided by default in traitAssertions
, which traitSuite
extends.This trait can be used to quickly solve a problem in which ScalaTest's default implicit conversion is clashing with those of some other library you need to use in your tests. After mixing in this trait, like this:
class MySuite extends FunSuite with NonImplicitAssertions { // ... }
You can write tests using
assert
(without triple equals),assertResult
,assertThrows
,intercept
,assertCompiles
,assertDoesNotCompile
, andassertTypeError
:assert(a < 7) assertResult(2) { 1 + 1 } assertThrows[IndexOutOfBoundsException] { "hi".charAt(-1) } val caught = intercept[IndexOutOfBoundsException] { "hi".charAt(-1) } assertDoesNotCompile("val a: String = 1") assertTypeError("val a: String = 1") assertCompiles("val a: Int = 1")
-
trait
Notifier
extends AnyRef
Trait providing an
apply
method to which status updates about a running suite of tests can be reported.Trait providing an
apply
method to which status updates about a running suite of tests can be reported.An
Notifier
is essentially used to wrap aReporter
and provide easy ways to send status updates to thatReporter
via anNoteProvided
event.Notifier
contains anapply
method that takes a string and an optional payload object of typeAny
. TheNotifier
will forward the passed alertmessage
string to theReporter
as themessage
parameter, and the optional payload object as thepayload
parameter, of anNoteProvided
event.For insight into the differences between
Notifier
,Alerter
, andInformer
, see the main documentation for traitNotifying
. -
trait
Notifying
extends AnyRef
Trait that contains the
note
method, which can be used to send a status notification to the reporter.Trait that contains the
note
method, which can be used to send a status notification to the reporter.The difference between
note
and theinfo
method ofInformer
is thatinfo
messages provided during a test are recorded and sent as part of test completion event, whereasnote
messages are sent right away asNoteProvided
messages. For long-running tests,note
allows you to send "status notifications" to the reporter right away, so users can track the progress of the long-running tests. By contrast,info
messages will only be seen by the user after the test has completed, and are more geared towards specification (such as Given/When/Then messages) than notification.The difference between
note
and thealert
method ofAlerting
is thatalert
is intended to be used for warnings or notifications of potential problems, whereasnote
is just for status notifications. In string reporters for which ANSI color is enabled,note
notifications are shown in green andalert
notifications in yellow. -
trait
OneInstancePerTest
extends SuiteMixin
Trait that facilitates a style of testing in which each test is run in its own instance of the suite class to isolate each test from the side effects of the other tests in the suite.
Trait that facilitates a style of testing in which each test is run in its own instance of the suite class to isolate each test from the side effects of the other tests in the suite.
Recommended Usage: Trait OneInstancePerTest
is intended primarily to serve as a supertrait forParallelTestExecution
and the path traits, to facilitate porting JUnit tests to ScalaTest, and to make it easy for users who prefer JUnit's approach to isolation to obtain similar behavior in ScalaTest.If you mix this trait into a
Suite
, you can initialize shared reassignable fixture variables as well as shared mutable fixture objects in the constructor of the class. Because each test will run in its own instance of the class, each test will get a fresh copy of the instance variables. This is the approach to test isolation taken, for example, by the JUnit framework.OneInstancePerTest
can, therefore, be handy when porting JUnit tests to ScalaTest.Here's an example of
OneInstancePerTest
being used in aFunSuite
:import org.scalatest.FunSuite import org.scalatest.OneInstancePerTest import collection.mutable.ListBuffer class MySuite extends FunSuite with OneInstancePerTest { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] test("easy") { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } test("fun") { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } }
OneInstancePerTest
is supertrait toParallelTestExecution
, in which running each test in its own instance is intended to make it easier to write suites of tests that run in parallel (by reducing the likelihood of concurrency bugs in those suites.)OneInstancePerTest
is also supertrait to the path traits,path.FunSpec
andpath.FreeSpec
, to make it obvious these traits run each test in a new, isolated instance.For the details on how
OneInstancePerTest
works, see the documentation for methodsrunTests
andrunTest
, which this trait overrides. -
trait
OptionValues
extends AnyRef
Trait that provides an implicit conversion that adds a
value
method toOption
, which will return the value of the option if it is defined, or throwTestFailedException
if not.Trait that provides an implicit conversion that adds a
value
method toOption
, which will return the value of the option if it is defined, or throwTestFailedException
if not.This construct allows you to express in one statement that an option should be defined and that its value should meet some expectation. Here's an example:
opt.value should be > 9
Or, using an assertion instead of a matcher expression:
assert(opt.value > 9)
Were you to simply invoke
get
on theOption
, if the option wasn't defined, it would throw aNoSuchElementException
:val opt: Option[Int] = None opt.get should be > 9 // opt.get throws NoSuchElementException
The
NoSuchElementException
would cause the test to fail, but without providing a stack depth pointing to the failing line of test code. This stack depth, provided byTestFailedException
(and a few other ScalaTest exceptions), makes it quicker for users to navigate to the cause of the failure. WithoutOptionValues
, to get a stack depth exception you would need to make two statements, like this:val opt: Option[Int] = None opt should be ('defined) // throws TestFailedException opt.get should be > 9
The
OptionValues
trait allows you to state that more concisely:val opt: Option[Int] = None opt.value should be > 9 // opt.value throws TestFailedException
-
sealed abstract
class
Outcome
extends Product with Serializable
Superclass for the possible outcomes of running a test.
Superclass for the possible outcomes of running a test.
Outcome
is the result type of thewithFixture
methods of traitsSuite
andfixture.Suite
, as well as theirNoArgTest
andOneArgTest
function types. The four possible outcomes are:Succeeded
- indicates a test succeededFailed
- indicates a test failed and contains an exception describing the failureCanceled
- indicates a test was canceled and contains an exception describing the cancelationPending
- indicates a test was pending
Note that "ignored" does not appear as a type of
Outcome
, because tests are marked as ignored on the outside and skipped over as the suite executes. So an ignored test never runs, and therefore never has an outcome. By contrast, a test is determined to be pending by running the test and observing the actual outcome. If the test body completes abruptly with aTestPendingException
, then the outcome was that the test was pending. -
trait
OutcomeOf
extends AnyRef
Trait that contains the
outcomeOf
method, which executes a passed code block and transforms the outcome into anOutcome
, using the same mechanism used by ScalaTest to produce anOutcome
when executing a test. -
trait
ParallelTestExecution
extends OneInstancePerTest
Trait that causes that the tests of any suite it is mixed into to be run in parallel if a
Distributor
is passed torunTests
.Trait that causes that the tests of any suite it is mixed into to be run in parallel if a
Distributor
is passed torunTests
.ScalaTest's normal approach for running suites of tests in parallel is to run different suites in parallel, but the tests of any one suite sequentially. This approach should provide sufficient distribution of the work load in most cases, but some suites may encapsulate multiple long-running tests. Such suites may dominate the execution time of the run. If so, mixing in this trait into just those suites will allow their long-running tests to run in parallel with each other, thereby helping to reduce the total time required to run an entire run.
To make it easier for users to write tests that run in parallel, this trait runs each test in its own instance of the class. Running each test in its own instance enables tests to use the same instance
vars
and mutable objects referenced from instance variables without needing to synchronize. Although ScalaTest provides functional approaches to factoring out common test code that can help avoid such issues, running each test in its own instance is an insurance policy that makes running tests in parallel easier and less error prone.For the details on how
ParallelTestExecution
works, see the documentation for methodsrun
,runTests
, andrunTest
, which this trait overrides.Note: This trait's implementation of
runTest
isfinal
, to ensure that behavior related to individual tests are executed by the same thread that executes the actual test. This means, for example, that you won't be allowed to write...with ParallelTestExecution with BeforeAndAfter
. Instead, you'd need to putParallelTestExecution
last, as in:with BeforeAndAfter with ParallelTestExecution
. For more details, see the documentation for therunTest
method. -
trait
PartialFunctionValues
extends AnyRef
Trait that provides an implicit conversion that adds a
valueAt
method toPartialFunction
, which will return the value (result) of the function applied to the argument passed tovalueAt
, or throwTestFailedException
if the partial function is not defined at the argument.Trait that provides an implicit conversion that adds a
valueAt
method toPartialFunction
, which will return the value (result) of the function applied to the argument passed tovalueAt
, or throwTestFailedException
if the partial function is not defined at the argument.This construct allows you to express in one statement that a partial function should be defined for a particular input, and that its result value should meet some expectation. Here's an example:
pf.valueAt("IV") should equal (4)
Or, using an assertion instead of a matcher expression:
assert(pf.valueAt("IV") === 4)
Were you to simply invoke
apply
on thePartialFunction
, passing in an input value, if the partial function wasn't defined at that input, it would throw some exception, but likely not one that provides a stack depth:// Note: a Map[K, V] is a PartialFunction[K, V] val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4) pf("V") should equal (5) // pf("V") throws NoSuchElementException
The
NoSuchElementException
thrown in this situation would cause the test to fail, but without providing a stack depth pointing to the failing line of test code. This stack depth, provided byTestFailedException
(and a few other ScalaTest exceptions), makes it quicker for users to navigate to the cause of the failure. WithoutPartialFunctionValues
, to get a stack depth exception you would need to make two statements, like this:val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4) pf.isDefinedAt("V") should be (true) // throws TestFailedException pf("V") should equal (5)
The
PartialFunctionValues
trait allows you to state that more concisely:val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4) pf.valueAt("V") should equal (5) // pf.valueAt("V") throws TestFailedException
-
trait
Payloads
extends AnyRef
Trait facilitating the inclusion of a payload in a thrown ScalaTest exception.
Trait facilitating the inclusion of a payload in a thrown ScalaTest exception.
This trait includes a
withPayload
construct that enables a payload object (or modified payload object) to be included as the payload of a thrown exception.Many ScalaTest events include an optional "payload" field that can be used to pass information to a custom reporter. This trait facilitates such customization, by making it easy to insert a payload into a thrown exception, such as a
TestFailedException
. The thrown exception must mix inPayload
. ScalaTest looks for traitPayload
and fires any payloads it finds in the relevant ScalaTest event stimulated by the exception, such as aTestFailed
event stimulated by aTestFailedException
. Here's an example in which a GUI snapshot is included as a payload when a test fails:withPayload(generateGUISnapshot()) { 1 + 1 should === (3) }
-
sealed
trait
PendingStatement
extends AnyRef
Trait mixed into the result type of the
pending
statement of traitAssertions
, which always throwsTestPendingException
.Trait mixed into the result type of the
pending
statement of traitAssertions
, which always throwsTestPendingException
.This type is used primarily to facilitate the
is (pending)
syntax of traitsFlatSpec
,WordSpec
, andFLatSpec
as well theis (pending)
or(pending)
syntax of sibling traits in theorg.scalatest.fixture
package. Because thepending
method inAssertions
always completes abruptly with an exception, its type would be inferred to beNothing
, which is a relatively common type. To make sure syntax likeis (pending)
only works with methodpending
, it is helpful to have a specially named "Nothing
" type. -
trait
PrivateMethodTester
extends AnyRef
Trait that facilitates the testing of private methods.
Trait that facilitates the testing of private methods.
To test a private method, mix in trait
PrivateMethodTester
and create aPrivateMethod
object, like this:val decorateToStringValue = PrivateMethod[String]('decorateToStringValue)
The type parameter on
PrivateMethod
, in this caseString
, is the result type of the private method you wish to invoke. The symbol passed to thePrivateMethod.apply
factory method, in this case'decorateToStringValue
, is the name of the private method to invoke. To test the private method, use theinvokePrivate
operator, like this:targetObject invokePrivate decorateToStringValue(1)
Here,
targetObject
is a variable or singleton object name referring to the object whose private method you want to test. You pass the arguments to the private method in the parentheses after thePrivateMethod
object. The result type of aninvokePrivate
operation will be the type parameter of thePrivateMethod
object, thus you need not cast the result to use it. In other words, after creating aPrivateMethod
object, the syntax to invoke the private method looks like a regular method invocation, but with the dot (.
) replaced byinvokePrivate
. The private method is invoked dynamically via reflection, so if you have a typo in the method name symbol, specify the wrong result type, or pass invalid parameters, theinvokePrivate
operation will compile, but throw an exception at runtime.One limitation to be aware of is that you can't use
PrivateMethodTester
to test a private method declared in a trait, because the class the trait gets mixed into will not declare that private method. Only the class generated to hold method implementations for the trait will have that private method. If you want to test a private method declared in a trait, and that method does not use any state of that trait, you can move the private method to a companion object for the trait and test it usingPrivateMethodTester
that way. If the private trait method you want to test uses the trait's state, your best options are to test it indirectly via a non-private trait method that calls the private method, or make the private method package access and test it directly via regular static method invocations.Also, if you want to use
PrivateMethodTester
to invoke a parameterless private method, you'll need to use empty parens. Instead of:targetObject invokePrivate privateParameterlessMethod
You'll need to write:
targetObject invokePrivate privateParameterlessMethod()
-
class
PropSpec
extends PropSpecLike
A suite of property-based tests.
A suite of property-based tests.
Recommended Usage: Class PropSpec
is a good fit for teams that want to write tests exclusively in terms of property checks, and is also a good choice for writing the occasional test matrix when a different style trait is chosen as the main unit testing style.Here's an example
PropSpec
:package org.scalatest.examples.propspec import org.scalatest._ import prop._ import scala.collection.immutable._ class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers { val examples = Table( "set", BitSet.empty, HashSet.empty[Int], TreeSet.empty[Int] ) property("an empty Set should have size 0") { forAll(examples) { set => set.size should be (0) } } property("invoking head on an empty set should produce NoSuchElementException") { forAll(examples) { set => a [NoSuchElementException] should be thrownBy { set.head } } } }
You can run a
PropSpec
by invokingexecute
on it. This method, which prints test results to the standard output, is intended to serve as a convenient way to run tests from within the Scala interpreter. For example, to runSetSpec
from within the Scala interpreter, you could write:scala> org.scalatest.run(new SetSpec)
And you would see:
SetSpec: - an empty Set should have size 0 - invoking head on an empty Set should produce NoSuchElementException
Or, to run just the “
an empty Set should have size 0
” method, you could pass that test's name, or any unique substring of the name, such as"size 0"
or even just"0"
. Here's an example:scala> org.scalatest.run(new SetSpec, "size 0") SetSpec: - an empty Set should have size 0
You can also pass to
execute
a config map of key-value pairs, which will be passed down into suites and tests, as well as other parameters that configure the run itself. For more information on running in the Scala interpreter, see the documentation forexecute
(below) and the ScalaTest shell.The
execute
method invokes arun
method that takes two parameters. Thisrun
method, which actually executes the suite, will usually be invoked by a test runner, such asrun
,tools.Runner
, a build tool, or an IDE.“
property
” is a method, defined inPropSpec
, which will be invoked by the primary constructor ofSetSpec
. 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 toproperty
, which registers it for later execution.A
PropSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered with the
property
method while thePropSpec
is in its registration phase. Any attempt to register a test after thePropSpec
has entered its ready phase, i.e., afterrun
has been invoked on thePropSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingPropSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.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,
PropSpec
provides registration methods that start withignore
instead ofproperty
. Here's an example:package org.scalatest.examples.suite.ignore import org.scalatest._ import prop._ import scala.collection.immutable._ class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers { val examples = Table( "set", BitSet.empty, HashSet.empty[Int], TreeSet.empty[Int] ) ignore("an empty Set should have size 0") { forAll(examples) { set => set.size should be (0) } } property("invoking head on an empty set should produce NoSuchElementException") { forAll(examples) { set => a [NoSuchElementException] should be thrownBy { set.head } } } }
If you run this version of
SetSuite
with:scala> org.scalatest.run(new SetSpec)
It will run only the second test and report that the first test was ignored:
SetSuite: - an empty Set should have size 0 !!! IGNORED !!! - invoking head on an empty Set should produce NoSuchElementException
Informers
One of the parameters to
PropSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byPropSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event. Here's an example that shows both a direct use as well as an indirect use through the methods ofGivenWhenThen
:package org.scalatest.examples.propspec.info import org.scalatest._ import prop._ import collection.mutable class SetSpec extends PropSpec with TableDrivenPropertyChecks with GivenWhenThen { val examples = Table( "set", mutable.BitSet.empty, mutable.HashSet.empty[Int], mutable.LinkedHashSet.empty[Int] ) property("an element can be added to an empty mutable Set") { forAll(examples) { set => info("—————-") Given("an empty mutable " + set.getClass.getSimpleName) assert(set.isEmpty) When("an element is added") set += 99 Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains(99)) } } }
If you run this
PropSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) SetSpec: - an element can be added to an empty mutable Set + ---------------- + Given an empty mutable BitSet + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + ---------------- + Given an empty mutable HashSet + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + ---------------- + Given an empty mutable LinkedHashSet + When an element is added + Then the Set should have size 1 + And the Set should contain the added element
Documenters
PropSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
PropSpec
that usesmarkup
:package org.scalatest.examples.propspec.markup import org.scalatest._ import prop._ import collection.mutable class SetSpec extends PropSpec with TableDrivenPropertyChecks with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } val examples = Table( "set", mutable.BitSet.empty, mutable.HashSet.empty[Int], mutable.LinkedHashSet.empty[Int] ) property("an element can be added to an empty mutable Set") { forAll(examples) { set => info("—————-") Given("an empty mutable " + set.getClass.getSimpleName) assert(set.isEmpty) When("an element is added") set += 99 Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains(99)) } markup("This test finished with a **bold** statement!") } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.propspec.note import org.scalatest._ import prop._ import collection.mutable class SetSpec extends PropSpec with TableDrivenPropertyChecks { val examples = Table( "set", mutable.BitSet.empty, mutable.HashSet.empty[Int], mutable.LinkedHashSet.empty[Int] ) property("an element can be added to an empty mutable Set") { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") forAll(examples) { set => assert(set.isEmpty) set += 99 assert(set.size === 1) assert(set.contains(99)) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: + notes are sent immediately + alerts are also sent immediately - an element can be added to an empty mutable Set + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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. (The code of a pending test is executed just like any other test.) However, because the test completes abruptly withTestPendingException
, the test will be reported as pending, to indicate the actual test, and possibly the functionality, has not yet been implemented.You can mark tests pending in
PropSpec
like this:import org.scalatest._ import prop._ import scala.collection.immutable._ class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers { val examples = Table( "set", BitSet.empty, HashSet.empty[Int], TreeSet.empty[Int] ) property("an empty Set should have size 0") (pending) property("invoking head on an empty set should produce NoSuchElementException") { forAll(examples) { set => a [NoSuchElementException] should be thrownBy { set.head } } } }
(Note: "
(pending)
" is the body of the test. Thus the test contains just one statement, an invocation of thepending
method, which throwsTestPendingException
.) If you run this version ofSetSuite
with:scala> org.scalatest.run(new SetSuite)
It will run both tests, but report that first test is pending. You'll see:
SetSuite: - An empty Set should have size 0 (pending) - Invoking head on an empty Set should produce NoSuchElementException
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait.Tagging tests
A
PropSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aPropSpec
, groups of tests can optionally be included and/or excluded. To tag aPropSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified names,com.mycompany.tags.DbTest
, then you could create a matching tag forPropSpec
s like this:package org.scalatest.examples.propspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
PropSpec
tests into groups with tags like this:import org.scalatest._ import prop._ import tagobjects.Slow import scala.collection.immutable._ class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers { val examples = Table( "set", BitSet.empty, HashSet.empty[Int], TreeSet.empty[Int] ) property("an empty Set should have size 0", Slow) { forAll(examples) { set => set.size should be (0) } } property("invoking head on an empty set should produce NoSuchElementException", Slow, DbTest) { forAll(examples) { set => a [NoSuchElementException] should be thrownBy { set.head } } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The techniques in
PropSpec
are identical to those inFunSuite
, but with “test
” replaced by “property
”. The following table summarizes the options with a link to the relevant documentation for traitFunSuite
:Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Using
PropSpec
to implement a test matrixUsing fixture-context objects in a
PropSpec
is a good way to implement a test matrix. What is the matrix? A test matrix is a series of tests that you need to run on a series of subjects. For example, The Scala API contains many implementations of traitSet
. Every implementation must obey the contract ofSet
. One property of anySet
is that an emptySet
should have size 0, another is that invoking head on an emptySet
should give you aNoSuchElementException
, and so on. Already you have a matrix, where rows are the properties and the columns are the set implementations:BitSet
HashSet
TreeSet
An empty Set should have size 0 pass pass pass Invoking head on an empty set should produce NoSuchElementException pass pass pass One way to implement this test matrix is to define a trait to represent the columns (in this case,
BitSet
,HashSet
, andTreeSet
) as elements in a single-dimensionalTable
. Each element in theTable
represents oneSet
implementation. Because different properties may require different fixture instances for those implementations, you can define a trait to hold the examples, like this:trait SetExamples extends Tables { def examples = Table("set", bitSet, hashSet, treeSet) def bitSet: BitSet def hashSet: HashSet[Int] def treeSet: TreeSet[Int] }
Given this trait, you could provide empty sets in one implementation of
SetExamples
, and non-empty sets in another. Here's how you might provide empty set examples:class EmptySetExamples extends SetExamples { def bitSet = BitSet.empty def hashSet = HashSet.empty[Int] def treeSet = TreeSet.empty[Int] }
And here's how you might provide set examples with one item each:
class SetWithOneItemExamples extends SetExamples { def bitSet = BitSet(1) def hashSet = HashSet(1) def treeSet = TreeSet(1) }
Armed with these example classes, you can define checks of properties that require empty or non-empty set fixtures by using instances of these classes as fixture-context objects. In other words, the columns of the test matrix are implemented as elements of a one-dimensional table of fixtures, the rows are implemented as
property
clauses of aPropSpec
.Here's a complete example that checks the two properties mentioned previously:
package org.scalatest.examples.propspec.matrix import org.scalatest._ import org.scalatest.prop._ import scala.collection.immutable._ trait SetExamples extends Tables { def examples = Table("set", bitSet, hashSet, treeSet) def bitSet: BitSet def hashSet: HashSet[Int] def treeSet: TreeSet[Int] } class EmptySetExamples extends SetExamples { def bitSet = BitSet.empty def hashSet = HashSet.empty[Int] def treeSet = TreeSet.empty[Int] } class SetSpec extends PropSpec with TableDrivenPropertyChecks with Matchers { property("an empty Set should have size 0") { new EmptySetExamples { forAll(examples) { set => set.size should be (0) } } } property("invoking head on an empty set should produce NoSuchElementException") { new EmptySetExamples { forAll(examples) { set => a [NoSuchElementException] should be thrownBy { set.head } } } } }
One benefit of this approach is that the compiler will help you when you need to add either a new row or column to the matrix. In either case, you'll need to ensure all cells are checked to get your code to compile.
Shared tests
Sometimes you may want to run the same test code on different fixture objects. That is to say, you may want to write tests that are "shared" by different fixture objects. You accomplish this in a
PropSpec
in the same way you would do it in aFunSuite
, except instead oftest
you sayproperty
, and instead oftestsFor
you saypropertiesFor
. For more information, see the Shared tests section ofFunSuite
's documentation. -
trait
PropSpecLike
extends TestSuite with TestRegistration with Informing with Notifying with Alerting with Documenting
Implementation trait for class
PropSpec
, which represents a suite of property-based tests.Implementation trait for class
PropSpec
, which represents a suite of property-based tests.PropSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofPropSpec
into some other class, you can use this trait instead, because classPropSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
PropSpec
. -
trait
RandomTestOrder
extends OneInstancePerTest
Trait that causes tests to be run in pseudo-random order.
Trait that causes tests to be run in pseudo-random order.
Although the tests are run in pseudo-random order, events will be fired in the “normal” order for the
Suite
that mixes in this trait, as determined byrunTests
.The purpose of this trait is to reduce the likelihood of unintentional order dependencies between tests in the same test class.
-
trait
RecoverMethods
extends AnyRef
Offers two methods for transforming futures when exceptions are expected.
Offers two methods for transforming futures when exceptions are expected.
This trait offers two methods for testing for expected exceptions in the context of futures:
recoverToSucceededIf
andrecoverToExceptionIf
. Because this trait is mixed into traitAsyncTestSuite
, both of its methods are available by default in any async-style suite.If you just want to ensure that a future fails with a particular exception type, and do not need to inspect the exception further, use
recoverToSucceededIf
:recoverToSucceededIf[IllegalStateException] { // Result type: Future[Assertion] emptyStackActor ? Peek }
The
recoverToSucceededIf
method performs a job similar toassertThrows
, except in the context of a future. It transforms aFuture
of any type into aFuture[Assertion]
that succeeds only if the original future fails with the specified exception. Here's an example in the REPL:scala> import org.scalatest.RecoverMethods._ import org.scalatest.RecoverMethods._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new IllegalStateException } | } res0: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res0.value res1: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Success(Succeeded))
Otherwise it fails with an error message similar to those given by
assertThrows
:scala> recoverToSucceededIf[IllegalStateException] { | Future { throw new RuntimeException } | } res2: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res2.value res3: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but java.lang.RuntimeException was thrown)) scala> recoverToSucceededIf[IllegalStateException] { | Future { 42 } | } res4: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res4.value res5: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: Expected exception java.lang.IllegalStateException to be thrown, but no exception was thrown))
The
recoverToExceptionIf
method differs from therecoverToSucceededIf
in its behavior when the assertion succeeds:recoverToSucceededIf
yields aFuture[Assertion]
, whereasrecoverToExceptionIf
yields aFuture[T]
, whereT
is the expected exception type.recoverToExceptionIf[IllegalStateException] { // Result type: Future[IllegalStateException] emptyStackActor ? Peek }
In other words,
recoverToExpectionIf
is tointercept
asrecovertToSucceededIf
is toassertThrows
. The first one allows you to perform further assertions on the expected exception. The second one gives you a result type that will satisfy the type checker at the end of the test body. Here's an example showingrecoverToExceptionIf
in the REPL:scala> val futureEx = | recoverToExceptionIf[IllegalStateException] { | Future { throw new IllegalStateException("hello") } | } futureEx: scala.concurrent.Future[IllegalStateException] = ... scala> futureEx.value res6: Option[scala.util.Try[IllegalStateException]] = Some(Success(java.lang.IllegalStateException: hello)) scala> futureEx map { ex => assert(ex.getMessage == "world") } res7: scala.concurrent.Future[org.scalatest.Assertion] = ... scala> res7.value res8: Option[scala.util.Try[org.scalatest.Assertion]] = Some(Failure(org.scalatest.exceptions.TestFailedException: "[hello]" did not equal "[world]"))
-
trait
Reporter
extends AnyRef
Trait whose instances collect the results of a running suite of tests and presents those results in some way to the user.
Trait whose instances collect the results of a running suite of tests and presents those results in some way to the user. Instances of this trait can be called "report functions" or "reporters."
Reporters receive test results via fifteen events. Each event is fired to pass a particular kind of information to the reporter. The events are:
DiscoveryStarting
DiscoveryCompleted
RunStarting
RunStopped
RunAborted
RunCompleted
ScopeOpened
ScopeClosed
ScopePending
TestStarting
TestSucceeded
TestFailed
TestCanceled
TestIgnored
TestPending
SuiteStarting
SuiteCompleted
SuiteAborted
InfoProvided
MarkupProvided
AlertProvided
NoteProvided
Reporters may be implemented such that they only present some of the reported events to the user. For example, you could define a reporter class that does nothing in response to
SuiteStarting
events. Such a class would always ignoreSuiteStarting
events.The term test as used in the
TestStarting
,TestSucceeded
, andTestFailed
event names is defined abstractly to enable a wide range of test implementations. ScalaTest's style traits (subclasse of traitSuite
) fireTestStarting
to indicate they are about to invoke one of their tests,TestSucceeded
to indicate a test returned normally, andTestFailed
to indicate a test completed abruptly with an exception. Although the execution of aSuite
subclass's tests will likely be a common event reported via theTestStarting
,TestSucceeded
, andTestFailed
events, because of the abstract definition of “test” used by the the event classes, these events are not limited to this use. Information about any conceptual test may be reported via theTestStarting
,TestSucceeded
, andTestFailed
events.Likewise, the term suite as used in the
SuiteStarting
,SuiteAborted
, andSuiteCompleted
event names is defined abstractly to enable a wide range of suite implementations. ObjectRunner
firesSuiteStarting
to indicate it is about to invokerun
on aSuite
,SuiteCompleted
to indicate aSuite
'srun
method returned normally, andSuiteAborted
to indicate aSuite
'srun
method completed abruptly with an exception. Similarly, classSuite
firesSuiteStarting
to indicate it is about to invokerun
on a nestedSuite
,SuiteCompleted
to indicate a nestedSuite
'srun
method returned normally, andSuiteAborted
to indicate a nestedSuite
'srun
method completed abruptly with an exception. Although the execution of aSuite
'srun
method will likely be a common event reported via theSuiteStarting
,SuiteAborted
, andSuiteCompleted
events, because of the abstract definition of "suite" used by the event classes, these events are not limited to this use. Information about any conceptual suite may be reported via theSuiteStarting
,SuiteAborted
, andSuiteCompleted
events.Extensibility
You can create classes that extend
Reporter
to report test results in custom ways, and to report custom information passed as an event "payload."Reporter
classes can handle events in any manner, including doing nothing. -
trait
ResourcefulReporter
extends Reporter
Subtrait of
Reporter
that contains adispose
method for releasing any finite, non-memory resources, such as file handles, held by theReporter
.Subtrait of
Reporter
that contains adispose
method for releasing any finite, non-memory resources, such as file handles, held by theReporter
.Runner
will invokedispose
on anyResourcefulReporter
when it no longer needs theReporter
. -
trait
Retries
extends AnyRef
Provides methods that can be used in
withFixture
implementations to retry tests in various scenarios.Provides methods that can be used in
withFixture
implementations to retry tests in various scenarios.Trait
Retries
is intended to help you deal with “flickers”—tests that usually pass, but occasionally fail. The best way to deal with such tests is to fix them so they always pass. Sometimes, however, this is not practical. In such cases, flickers can waste your time by forcing you to investigate test failures that turn out to be flickers. Or worse, like the boy who cried wolf, the flickers may train you an your colleagues to not pay attention to failures such that you don't notice real problems, at least not in a timely manner.Trait
Retries
offers methods that will retry a failed and/or canceled test once, on the same thread, with or without a delay. These methods take a block that results inOutcome
, and are intended to be used inwithFixture
methods. You should be very selective about which tests you retry, retrying those for which you have good evidence to conclude they are flickers. Thus it is recommended you only retry tests that are tagged withRetryable
, and only tag tests as such once they have flickered consistently for a while, and only after you invested a reasonable effort into fixing them properly.Here's an example showing how you might use
Retries
:package org.scalatest.examples.tagobjects.retryable import org.scalatest._ import tagobjects.Retryable class SetSpec extends FlatSpec with Retries { override def withFixture(test: NoArgTest) = { if (isRetryable(test)) withRetry { super.withFixture(test) } else super.withFixture(test) } "An empty Set" should "have size 0" taggedAs(Retryable) in { assert(Set.empty.size === 0) } }
-
class
Sequential
extends Suite with SequentialNestedSuiteExecution
A
Suite
class mixing inSequentialNestedSuiteExecution
that takes zero to manySuite
s, which will be returned from itsnestedSuites
method.A
Suite
class mixing inSequentialNestedSuiteExecution
that takes zero to manySuite
s, which will be returned from itsnestedSuites
method.For example, you can define a suite that always executes a list of nested suites like this:
class StepsSuite extends Sequential( new Step1Suite, new Step2Suite, new Step3Suite, new Step4Suite, new Step5Suite )
When
StepsSuite
is executed, it will execute its nested suites in the passed order:Step1Suite
,Step2Suite
,Step3Suite
,Step4Suite
, andStep5Suite
. BecauseSequential
extendsSequentialNestedSuiteExecution
, the distributor passed torunNestedSuites
will always beNone
. So not only will the suites passed to the constructor be executed sequentially, any tests and nested suites of the passed suites will also be executed sequentually.The difference between
Sequential
andStepwise
is that althoughStepwise
executes its own nested suites sequentially, it passes whatever distributor was passed to it to those nested suites. Thus the nested suites could run their own nested suites and tests in parallel if that distributor is defined. By contrast,Sequential
always passesNone
for the distributor to the nested suites, so any and every test and nested suite contained within the nested suites passed to theSequential
construtor will be executed sequentially.- Exceptions thrown
NullArgumentException
ifsuitesToNest
, or any suite it contains, isnull
.
-
trait
SequentialNestedSuiteExecution
extends SuiteMixin
Trait that causes the nested suites of any suite it is mixed into to be run sequentially even if a
Distributor
is passed torunNestedSuites
.Trait that causes the nested suites of any suite it is mixed into to be run sequentially even if a
Distributor
is passed torunNestedSuites
. This trait overrides therunNestedSuites
method and fowards every parameter passed to it to a superclass invocation ofrunNestedSuites
, except it always passesNone
for theDistributor
. Mix in this trait into any suite whose nested suites need to be run sequentially even with the rest of the run is being executed concurrently. -
trait
SeveredStackTraces
extends TestSuiteMixin
Trait that causes
StackDepth
exceptions thrown by a running test (such asTestFailedException
s) to have the exception's stack trace severed at the stack depth.Trait that causes
StackDepth
exceptions thrown by a running test (such asTestFailedException
s) to have the exception's stack trace severed at the stack depth. Because the stack depth indicates the exact line of code that caused the exception to be thrown, the severed stack trace will show that offending line of code on top. This can make the line of test code that discovered a problem to be more easily found in IDEs and tools that don't make use of ScalaTest'sStackDepth
exceptions directly. -
sealed
trait
Shell
extends AnyRef
Trait whose instances provide a
run
method and configuration fields that implement the ScalaTest shell: its DSL for the Scala interpreter.Trait whose instances provide a
run
method and configuration fields that implement the ScalaTest shell: its DSL for the Scala interpreter.The main command of the ScalaTest shell is
run
, which you can use to run a suite of tests. The shell also provides several commands for configuring a call torun
:color
(the default) - display results in color (green for success; red for failure; yellow for warning; blue for statistics)nocolor
- display results without colordurations
- display durations of (i.e., how long it took to run) tests and suitesnodurations
(the default) - do not display durations of tests and suitesshortstacks
- display short (i.e., truncated to show just the most useful portion) stack traces for all exceptionsfullstacks
- display full stack trackes for all exceptionsnostacks
(the default) - display no stack trace forStackDepth
exceptions and a short stack trace for non-StackDepth
exceptionsstats
- display statistics before and after the run, such as expected test count before the run and tests succeeded, failed, pending, etc., counts after the runnostats
(the default) not display statistics before or after the run
The default configuration is
color
,nodurations
,nostacks
, andnostats
.All of these commands are fields of trait
org.scalatest.Shell
. Each configuration command is a field that refers to anotherShell
instance with every configuration parameter the same except for the one you've asked to change. For example,durations
provides aShell
instance that has every parameter configured the same way, except with durations enabled. When you invokerun
on that, you will get a run with durations enabled and every other configuration parameter at its default value.The other useful "command" to know about, though not technically part of the shell, is the
apply
factory method in theSuites
singleton object. This allows you to easily create composite suites out of nested suites, which you can then pass torun
. This will be demonstrated later in this documentation.Using the ScalaTest shell
The package object of the
org.scalatest
package, although it does not extendShell
, declares all the same members asShell
. Itsrun
method runs with all theShell
configuration parameters set to their default values. A good way to use the ScalaTest shell, therefore, is to import the members of packageorg.scalatest
:scala> import org.scalatest._ import org.scalatest._
One thing importing
org.scalatest._
allows you to do is access any of ScalaTest's classes and traits by shorter names, for example:scala> class ArithmeticSuite extends FunSuite with matchers.Matchers { | test("addition works") { | 1 + 1 should equal (2) | } | ignore("subtraction works") { | 1 - 1 should equal (0) | } | test("multiplication works") { | 1 * 1 should equal (2) | } | test("division works") (pending) | } defined class ArithmeticSuite
But importing
org.scalatest._
also brings into scope the commands of theShell
, so you can, for example, invokerun
without qualification:scala> run(new ArithmeticSuite) ArithmeticSuite: - addition works - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** 1 did not equal 2 (
:16) - division works (pending)Configuring a single run
To configure a single run, you can prefix run by one or more configuration commands, separated by dots. For example, to enable durations during a single run, you would write:
scala> durations.run(new ArithmeticSuite) ArithmeticSuite: - addition works (102 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (36 milliseconds) 1 did not equal 2 (
:16) - division works (pending)To enable statistics during a single run, you would write:
scala> stats.run(new ArithmeticSuite) Run starting. Expected test count is: 3 ArithmeticSuite: - addition works - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** 1 did not equal 2 (
:16) - division works (pending) Run completed in 386 milliseconds. Total number of tests run: 2 Suites: completed 1, aborted 0 Tests: succeeded 1, failed 1, ignored 1, pending 1 *** 1 TEST FAILED ***And to enable both durations and statistics during a single run, you could write:
scala> durations.stats.run(new ArithmeticSuite) Run starting. Expected test count is: 3 ArithmeticSuite: - addition works (102 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED (36 milliseconds)*** 1 did not equal 2 (
:16) - division works (pending) Run completed in 386 milliseconds. Total number of tests run: 2 Suites: completed 1, aborted 0 Tests: succeeded 1, failed 1, ignored 1, pending 1 *** 1 TEST FAILED ***The order doesn't matter when you are chaining multiple configuration commands. You'll get the same result whether you write
durations.stats.run
orstats.durations.run
.To disable color, use
nocolor
:scala> nocolor.run(new ArithmeticSuite) ArithmeticSuite: - addition works - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** 1 did not equal 2 (
:16) - division works (pending) To enable short stack traces during a single run, use
shortstacks
:scala> shortstacks.run(new ArithmeticSuite) ArithmeticSuite: - addition works (101 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (33 milliseconds) 1 did not equal 2 (
:16) org.scalatest.exceptions.TestFailedException: ... at line2$object$$iw$$iw$$iw$$iw$ArithmeticSuite$$anonfun$3.apply$mcV$sp( - division works (pending):16) at line2$object$$iw$$iw$$iw$$iw$ArithmeticSuite$$anonfun$3.apply( :16) at line2$object$$iw$$iw$$iw$$iw$ArithmeticSuite$$anonfun$3.apply( :16) at org.scalatest.FunSuite$$anon$1.apply(FunSuite.scala:992) at org.scalatest.Suite$class.withFixture(Suite.scala:1661) at line2$object$$iw$$iw$$iw$$iw$ArithmeticSuite.withFixture( :8) at org.scalatest.FunSuite$class.invokeWithFixture$1(FunSuite.scala:989) ... Changing the default configuration
If you want to change the default for multiple runs, you can import the members of your favorite
Shell
configuration. For example, if you always like to run with durations and statistics enabled, you could write:scala> import stats.durations._ import stats.durations._
Now anytime you run statistics and durations will, by default, be enabled:
scala> run(new ArithmeticSuite) Run starting. Expected test count is: 3 ArithmeticSuite: - addition works (9 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (10 milliseconds) 1 did not equal 2 (
:18) - division works (pending) Run completed in 56 milliseconds. Total number of tests run: 2 Suites: completed 1, aborted 0 Tests: succeeded 1, failed 1, ignored 1, pending 1 *** 1 TEST FAILED ***Running multiple suites
If you want to run multiple suites, you can use the factory method in the
Suites
singleton object. If you wrap a comma-separated list of suite instances insideSuites(...)
, for example, you'll get a suite instance that contains no tests, but whose nested suites includes the suite instances you placed between the parentheses. You can placeSuites
insideSuites
to any level of depth, creating a tree of suites to pass torun
. Here's a (contrived) example in whichArithmeticSuite
is executed four times:scala> run(Suites(new ArithmeticSuite, new ArithmeticSuite, Suites(new ArithmeticSuite, new ArithmeticSuite))) Run starting. Expected test count is: 12 Suites: ArithmeticSuite: - addition works (0 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (1 millisecond) 1 did not equal 2 (
:16) - division works (pending) ArithmeticSuite: - addition works (1 millisecond) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (0 milliseconds) 1 did not equal 2 (:16) - division works (pending) Suites: ArithmeticSuite: - addition works (0 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (0 milliseconds) 1 did not equal 2 (:16) - division works (pending) ArithmeticSuite: - addition works (0 milliseconds) - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** (0 milliseconds) 1 did not equal 2 (:16) - division works (pending) Run completed in 144 milliseconds. Total number of tests run: 8 Suites: completed 6, aborted 0 Tests: succeeded 4, failed 4, ignored 4, pending 4 *** 4 TESTS FAILED ***Running a single test
The
run
command also allows you to specify the name of a test to run and/or a config map. You can run a particular test in a suite, for example, by specifying the test name after the suite instance in your call torun
, like this:scala> run(new ArithmeticSuite, "addition works") ArithmeticSuite: - addition works
-
final
class
StatefulStatus
extends Status with Serializable
Status implementation that can change its state over time.
Status implementation that can change its state over time.
A
StatefulStatus
begins its life in a successful state, and will remain successful unlesssetFailed
is called. OncesetFailed
is called, the status will remain at failed. ThesetFailed
method can be called multiple times (even though invoking it once is sufficient to permanently set the status to failed), but only up untilsetCompleted
has been called. AftersetCompleted
has been called, any invocation ofsetFailed
will be greeted with anIllegalStateException
.Instances of this class are thread safe.
-
sealed
trait
Status
extends AnyRef
The result status of running a test or a suite, which is used to support parallel and asynchronous execution of tests.
The result status of running a test or a suite, which is used to support parallel and asynchronous execution of tests.
This trait is the result type of the "run" lifecycle methods of trait
Suite
:run
,runNestedSuites
,runTests
, andrunTest
. It can be used to determine whether a test or suite has completed, and if so, whether it succeeded, and if not, whether an exception was thrown that was not yet reported via a ScalaTest event. AStatus
is like a domain-specificFuture[Boolean]
, where:- an activity in which no test failed and no suite aborted is represented by
Success(true)
- an activity during which at least one test failed or one suite aborted, but all exceptions that occured
were reported by a ScalaTest events (such as
TestFailedException
) is represented bySuccess(false)
- an activity during which at least one test failed or one suite aborted and at least one exception occurred that was
not reported via a ScalaTest event is represented by
Failure(unreportedException)
Note that pending and canceled tests will not cause a
Status
to fail. Only failed tests and aborted suites will cause aStatus
to fail.One use case of
Status
is to ensure that "after" code (such as anafterEach
orafterAll
method) does not execute until after the relevant entity (one test, one suite, or all of a suite's tests or nested suites) has completed. Another use case is to implement the default behavior of asynchronous styles, in which subsequent each test does not begin execution until after the previous test has completed. - an activity in which no test failed and no suite aborted is represented by
-
class
Stepwise
extends Suite with StepwiseNestedSuiteExecution
A
Suite
class that takes zero to manySuite
s, which will be returned from itsnestedSuites
method and executed in “stepwise” fashion by itsrunNestedSuites
method.A
Suite
class that takes zero to manySuite
s, which will be returned from itsnestedSuites
method and executed in “stepwise” fashion by itsrunNestedSuites
method.For example, you can define a suite that always executes a list of nested suites like this:
class StepsSuite extends Stepwise( new Step1Suite, new Step2Suite, new Step3Suite, new Step4Suite, new Step5Suite )
When
StepsSuite
is executed, regardless of whether aDistributor
is passed, it will execute its nested suites sequentially in the passed order:Step1Suite
,Step2Suite
,Step3Suite
,Step4Suite
, andStep5Suite
.The difference between
Stepwise
andSequential
is that althoughStepwise
executes its own nested suites sequentially, it passes whatever distributor was passed to it to those nested suites. Thus the nested suites could run their own nested suites and tests in parallel if that distributor is defined. By contrast,Sequential
always passesNone
for the distributor to the nested suites, so any and every test and nested suite contained within the nested suites passed to theSequential
construtor will be executed sequentially.- Exceptions thrown
NullArgumentException
ifsuitesToNest
, or any suite it contains, isnull
.
-
trait
StepwiseNestedSuiteExecution
extends SuiteMixin
Trait that causes the nested suites of any suite it is mixed into to be run sequentially even if a
Distributor
is passed torunNestedSuites
.Trait that causes the nested suites of any suite it is mixed into to be run sequentially even if a
Distributor
is passed torunNestedSuites
. This trait overrides therunNestedSuites
method and fowards every parameter passed to it to a superclass invocation ofrunNestedSuites
, except it always passesNone
for theDistributor
. Mix in this trait into any suite whose nested suites need to be run sequentially even with the rest of the run is being executed concurrently. -
trait
Stopper
extends AnyRef
Trait whose instances can accept a stop request and indicate whether a stop has already been requested.
Trait whose instances can accept a stop request and indicate whether a stop has already been requested.
This is passed in to the
run
method ofSuite
, so that running suites of tests can be requested to stop early. -
trait
StreamlinedXml
extends AnyRef
Trait providing a
streamlined
method that returns aUniformity[T]
instance for any subtype ofscala.xml.NodeSeq
that will normalize the XML by removing empty text nodes and trimming non-empty text nodes.Trait providing a
streamlined
method that returns aUniformity[T]
instance for any subtype ofscala.xml.NodeSeq
that will normalize the XML by removing empty text nodes and trimming non-empty text nodes.Here's an example of some unnormalized XML:
<summer> <day></day> <night> with lots of stars </night> </summer>
The
Uniformity
returned by this trait'sstreamlined
method would transform the above XML to:<summer><day></day><night>with lots of stars</night></summer>
The
streamlined
method can be used with theExplicitly
DSL, like this:xmlElem should equal ( <summer> <day></day> <night> with lots of stars </night> </summer> ) (after being streamlined[Elem])
The goal of this trait is to provide a normalization for XML that makes it easier to test XML objects for equality. White space is significant in XML, and is taken into account by the default equality for XML, accessed by invoking the
==
method on an XMLNodeSeq
. Here's an example:scala> val xmlElem = <summer><day></day><night>with lots of stars</night></summer> xmlElem: scala.xml.Elem = <summer><day></day><night>with lots of stars</night></summer> scala> xmlElem == <summer> | <day></day> | <night> | with lots of stars | </night> | </summer> res1: Boolean = false
The above equality comparison produces false because of whitespace differences in the XML. When such whitespace differences are unimportant to the actual application, it can make it easier to write readable test code if you can compare XML for equality without taking into account empty text nodes, or leading and trailing whitespace in nonempty text nodes. The
streamlined
method of this trait provides aUniformity
instance that does just that:scala> import org.scalactic._ import org.scalactic._ scala> import Explicitly._ import Explicitly._ scala> import TripleEquals._ import TripleEquals._ scala> import org.scalatest.StreamlinedXml._ import StreamlinedXml._ scala> import scala.xml.Elem import scala.xml.Elem * scala> (xmlElem === <summer> | <day></day> | <night> | with lots of stars | </night> | </summer>) (after being streamlined[Elem]) res9: Boolean = true
-
trait
StreamlinedXmlEquality
extends AnyRef
Trait providing an implicit
Equality[T]
for subtypes ofscala.xml.NodeSeq
that before testing for equality, will normalize left and right sides by removing empty XML text nodes and trimming non-empty text nodes.Trait providing an implicit
Equality[T]
for subtypes ofscala.xml.NodeSeq
that before testing for equality, will normalize left and right sides by removing empty XML text nodes and trimming non-empty text nodes.Here's an example of some unnormalized XML:
<summer> <day></day> <night> with lots of stars </night> </summer>
Prior to testing it for equality, the implicit
Equality[T]
provided by this trait would transform the above XML to:<summer><day></day><night>with lots of stars</night></summer>
The goal of this trait is to provide an implicit
Equality
for XML that makes it easier to write tests involving XML. White space is significant in XML, and is taken into account by the default equality for XML, accessed by invoking the==
method on an XMLNodeSeq
. Here's an example:scala> val xmlElem = <summer><day></day><night>with lots of stars</night></summer> xmlElem: scala.xml.Elem = <summer><day></day><night>with lots of stars</night></summer> scala> import org.scalatest.Assertions._ import org.scalatest.Assertions._ scala> assert(xmlElem === <summer> | <day></day> | <night> | with lots of stars | </night> | </summer>) org.scalatest.exceptions.TestFailedException: <summer><day></day><night>with lots of stars</night></summer> did not equal <summer> <day></day> <night> with lots of stars </night> </summer> at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:500) at org.scalatest.Assertions$.newAssertionFailedException(Assertions.scala:1538) at org.scalatest.Assertions$AssertionsHelper.macroAssert(Assertions.scala:466) ... 53 elided
The above assertion fails because of whitespace differences in the XML. When such whitespace differences are unimportant to the actual application, it can make it easier to write readable test code if you can compare XML for equality without taking into account empty text nodes, or leading and trailing whitespace in nonempty text nodes. This trait provides an
Equality[T]
instance that does just that:scala> import org.scalatest.StreamlinedXmlEquality._ import org.scalatest.StreamlinedXmlEquality._ scala> assert(xmlElem === <summer> | <day></day> | <night> | with lots of stars | </night> | </summer>)
-
trait
StreamlinedXmlNormMethods
extends StreamlinedXml with NormMethods
Subtrait of
NormMethods
that provides an implicitUniformity[T]
for subtypes ofscala.xml.NodeSeq
that enables you to streamline XML by invoking.norm
on it.Subtrait of
NormMethods
that provides an implicitUniformity[T]
for subtypes ofscala.xml.NodeSeq
that enables you to streamline XML by invoking.norm
on it.Here's an example:
scala> <good><day>sunshine</day></good> == <good> | <day> | sunshine | </day> | </good> res1: Boolean = false scala> import org.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> import org.scalatest.StreamlinedXmlNormMethods._ import org.scalatest.StreamlinedXmlNormMethods._ scala> <good><day>sunshine</day></good> === <good> | <day> | sunshine | </day> | </good>.norm res2: Boolean = true
-
trait
Suite
extends Assertions with Serializable
A suite of tests.
A suite of tests. A
Suite
instance encapsulates a conceptual suite (i.e., a collection) of tests.This trait provides an interface composed of "lifecycle methods" that allow suites of tests to be run. Its implementation enables a default way of writing and executing tests. Subtraits and subclasses can override
Suite
's lifecycle methods to enable other ways of writing and executing tests.Nested suites
A
Suite
can refer to a collection of otherSuite
s, which are called nestedSuite
s. Those nestedSuite
s can in turn have their own nestedSuite
s, and so on. Large test suites can be organized, therefore, as a tree of nestedSuite
s. This trait'srun
method, in addition to invoking its test methods, invokesrun
on each of its nestedSuite
s.A
List
of aSuite
's nestedSuite
s can be obtained by invoking itsnestedSuites
method. If you wish to create aSuite
that serves as a container for nestedSuite
s, whether or not it has test methods of its own, simply overridenestedSuites
to return aList
of the nestedSuite
s. Because this is a common use case, ScalaTest provides a convenienceSuites
class, which takes a variable number of nestedSuite
s as constructor parameters. Here's an example:package org.scalatest.examples.suite.nested import org.scalatest._ class ASuite extends FunSuite { test("A should have ASCII value 41 hex") { assert('A' === 0x41) } test("a should have ASCII value 61 hex") { assert('a' === 0x61) } } class BSuite extends FunSuite { test("B should have ASCII value 42 hex") { assert('B' === 0x42) } test("b should have ASCII value 62 hex") { assert('b' === 0x62) } } class CSuite extends FunSuite { test("C should have ASCII value 43 hex") { assert('C' === 0x43) } test("c should have ASCII value 63 hex") { assert('c' === 0x63) } } class ASCIISuite extends Suites( new ASuite, new BSuite, new CSuite )
If you now run
ASCIISuite
:scala> org.scalatest.run(new ASCIISuite)
You will see reports printed to the standard output that indicate the nested suites—
ASuite
,BSuite
, andCSuite
—were run:ASCIISuite: ASuite: - A should have ASCII value 41 hex - a should have ASCII value 61 hex BSuite: - B should have ASCII value 42 hex - b should have ASCII value 62 hex CSuite: - C should have ASCII value 43 hex - c should have ASCII value 63 hex
Note that
Runner
can discoverSuite
s automatically, so you need not necessarily define nestedSuites
explicitly. See the documentation forRunner
for more information.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 a config map of key-value pairs, which is passed to
run
as aConfigMap
. 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 torun
,runNestedSuites
,runTests
, andrunTest
, 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 access it from theNoArgTest
passed towithFixture
, or theOneArgTest
passed towithFixture
in the traits in theorg.scalatest.fixture
package. (See the documentation forfixture.Suite
for instructions on how to access the config map in tests.)Executing suites in parallel
The
run
method takes as one of its parameters an optionalDistributor
. If aDistributor
is passed in, this trait's implementation ofrun
puts its nestedSuite
s into the distributor rather than executing them directly. The caller ofrun
is responsible for ensuring that some entity runs theSuite
s placed into the distributor. The-P
command line parameter toRunner
, for example, will causeSuite
s put into theDistributor
to be run in parallel via a pool of threads. If you wish to execute the tests themselves in parallel, mix inParallelTestExecution
."Run-aborting" exceptions
The Javadoc documentation for
java.lang.Error
states:An
Error
is a subclass ofThrowable
that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.Because
Error
s are used to denote serious errors, traitSuite
and its subtypes in the ScalaTest API do not always treat a test that completes abruptly with anError
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 anOutOfMemoryError
, it will not be reported as a test failure, but will instead cause the run to abort. Because not everyone usesError
s only to represent serious problems, however, ScalaTest only behaves this way for the following run-aborting 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
Error
s that exist as part of Java 1.5 API, excludingjava.lang.AssertionError
. ScalaTest does treat a thrownAssertionError
as an indication of a test failure. In addition, any otherError
that is not an instance of a type mentioned in the previous list will be caught by theSuite
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 toError
s, this behavior is not required by the contract ofSuite
. Subclasses and subtraits that you define, for example, may treat allError
s 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 theSuite
's test names in a custom way.tags
- override this method to specify theSuite
's test tags in a custom way.nestedSuites
- override this method to specify theSuite
's nestedSuite
s in a custom way.suiteName
- override this method to specify theSuite
's name in a custom way.expectedTestCount
- override this method to count thisSuite
's expected tests in a custom way.
For example, this trait's implementation of
testNames
performs reflection to discover methods starting withtest
, and places these in aSet
whose iterator returns the names in alphabetical order. If you wish to run tests in a different order in a particularSuite
, perhaps because a test namedtestAlpha
can only succeed after a test namedtestBeta
has run, you can overridetestNames
so that it returns aSet
whose iterator returnstestBeta
beforetestAlpha
. (This trait's implementation ofrun
will invoke tests in the order they come out of thetestNames
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 overridetestNames
to discover tests using either of these two APIs@Test
annotations, or one of your own invention. (This is in fact howorg.scalatest.junit.JUnitSuite
andorg.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
overridestestNames
,runTest
, andrun
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
andorg.scalatest.testng
exist to make this easy. No matter what legacy tests you may have, it is likely you can create or use an existingSuite
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. -
trait
SuiteMixin
extends AnyRef
Trait defining abstract "lifecycle" methods that are implemented in
Suite
and can be overridden in stackable modification traits.Trait defining abstract "lifecycle" methods that are implemented in
Suite
and can be overridden in stackable modification traits.The main purpose of
SuiteMixin
is to differentiate coreSuite
style traits, such asSpec
,FunSuite
, andFunSpec
from stackable modification traits forSuite
s such asBeforeAndAfterEach
,OneInstancePerTest
, andSequentialNestedSuiteExecution
. Because these stackable traits extendSuiteMixin
instead ofSuite
, you can't define a suite by simply extending one of the stackable traits:class MySuite extends BeforeAndAfterEach // Won't compile
Instead, you need to extend a core
Suite
trait and mix the stackableBeforeAndAfterEach
trait into that, like this:class MySuite extends FunSuite with BeforeAndAfterEach // Compiles fine
-
class
Suites
extends Suite
A
Suite
class that takes zero to manySuite
s in its constructor, which will be returned from itsnestedSuites
method.A
Suite
class that takes zero to manySuite
s in its constructor, which will be returned from itsnestedSuites
method.For example, you can define a suite that always executes a list of nested suites like this:
class StepsSuite extends Suites( new Step1Suite, new Step2Suite, new Step3Suite, new Step4Suite, new Step5Suite )
If
StepsSuite
is executed sequentially, it will execute its nested suites in the passed order:Step1Suite
,Step2Suite
,Step3Suite
,Step4Suite
, andStep5Suite
. IfStepsSuite
is executed in parallel, the nested suites will be executed concurrently.- Exceptions thrown
NullPointerException
ifsuitesToNest
, or any suite it contains, isnull
.
-
class
Tag
extends AnyRef
Class whose subclasses can be used to tag tests in style traits in which tests are defined as functions.
Class whose subclasses can be used to tag tests in style traits in which tests are defined as functions.
ScalaTest has two ways to tag tests: annotations and instances of this
Tag
class. To tag a test method or an entire test class, you use a tag annotation, whereas to tag a test function, you use aTag
object. Though not required, it is usually a good idea to define both an annotation and a correspondingTag
object for each conceptual tag you want, so you can tag anything: test functions, test classes, and test methods. The name of the conceptual tag is the fully qualified name of the annotation interface, so you must pass this name to theTag
constructor.For example, imagine you want to tag integration tests that use the actual database, and are, therefore, generally slower. You could create a tag annotation and object called
DbTest
. To give them both the same simple name, you can declare them in different packages. The tag annotation must be written in Java, not Scala, because annotations written in Scala are not accessible at runtime. Here's an example:package com.mycompany.myproject.testing.tags; import java.lang.annotation.*; import org.scalatest.TagAnnotation @TagAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface DbTest {}
Given this annotation's fully qualified name is
com.mycompany.myproject.testing.tags.DbTest
the correspondingTag
object decaration must have that name passed to its constructor, like this:package com.mycompany.myproject.testing.tagobjects object DbTest extends Tag("com.mycompany.myproject.testing.tags.DbTest")
Given these definitions, you could tag a test function as a
DbTest
in, for example, aFlatSpec
like this:import org.scalatest.FlatSpec import com.mycompany.myproject.testing.tagobjects.DbTest class ExampleSpec extends FlatSpec { "Integration tests" can "sometimes be slow" taggedAs(DbTest) in { Thread.sleep(1000) } }
You could tag a test method as a
DbTest
in, for example, aSuite
like this:import org.scalatest.Suite import com.mycompany.myproject.testing.tags.DbTest class ExampleSuite extends Suite { @DbTest def `integration tests can sometimes be slow` { Thread.sleep(1000) } }
And you could tag all the tests in an entire test class by annotating the class, like this:
import org.scalatest.FlatSpec import com.mycompany.myproject.testing.tags.DbTest @DBTest class ExampleSpec extends FlatSpec { "Integration tests" can "sometimes be slow" in { Thread.sleep(1000) } they should "likely sometimes be excluded " in { Thread.sleep(1000) } }
In the previous example, both tests will be tagged as
DBTest
s even though the tests are not tagged as such individually.When you run ScalaTest and want to either include or exclude
DbTest
s, you'd give the fully qualified name of the tag annotation (which is also the name passed to the correspondingTag
constructor) toRunner
. For example, here's how you'd excludeDbTest
s on theRunner
command line:-l com.mycompany.myproject.testing.tags.DbTest
For examples of tagging in other style traits, see the "Tagging tests" section in the documentation for the trait:
-
trait
TagAnnotation
extends Annotation
Annotation used to annotate annotation interfaces that define tags for ScalaTest tests.
Annotation used to annotate annotation interfaces that define tags for ScalaTest tests.
Note: This is actually an annotation defined in Java, not a Scala trait. It must be defined in Java instead of Scala so it will be accessible at runtime. It has been inserted into Scaladoc by pretending it is a trait.
ScalaTest will only consider annotations that are themselves annotated with
TagAnnotation
as tag annotations, to avoid accidentally interpreting arbitrary annotations as tags. You useTagAnnotation
, therefore, when you define a tag annotation (which you must do in Java). Here's an example:package com.mycompany.myproject.testing.tags; import java.lang.annotation.*; import org.scalatest.TagAnnotation * @TagAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface DbTest {}
For more information, see the documentation for class
Tag
. -
trait
TestData
extends AnyRef
A bundle of information about the current test.
A bundle of information about the current test.
A
TestData
object is passed to thewithFixture
methods of traitsSuite
andfixture.Suite
(bothNoArgTest
andOneArgTest
extendTestData
) and to thebeforeEach
andafterEach
methods of traitBeforeAndAfterEach
. This enables fixtures and tests to make use of the test name and configuration objects in the config map.In ScalaTest's event model, a test may be surrounded by “scopes.” Each test and scope is associated with string of text. A test's name is a concatenation of the text of any surrounding scopes followed by the text provided with the test itself, after each text element has been trimmed and one space inserted between each component. Here's an example:
package org.scalatest.examples.freespec import org.scalatest.FreeSpec class SetSpec extends FreeSpec { "A Set" - { "when empty" - { "should have size 0" in { assert(Set.empty.size === 0) } "should produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
The above
FreeSpec
contains two tests, both nested inside the same two scopes. The outermost scope names the subject,A Set
. The nested scope qualifies the subject withwhen empty
. Inside that scope are the two tests. The text of the tests are:should have size 0
should produce NoSuchElementException when head is invoked
Therefore, the names of these two tests are:
A Stack when empty should have size 0
A Stack when empty should produce NoSuchElementException when head is invoked
The
TestData
instance for the first test would contain:name
:"A Stack when empty should have size 0"
scopes
:collection.immutable.IndexedSeq("A Stack", "when empty")
text
:"should have size 0"
-
trait
TestRegistration
extends AnyRef
Trait declaring methods that can be used to register by-name test functions that have any result type.
-
trait
TestSuite
extends Suite
The base trait of ScalaTest's synchronous testing styles, which defines a
withFixture
lifecycle method that accepts as its parameter a test function that returns anOutcome
.The base trait of ScalaTest's synchronous testing styles, which defines a
withFixture
lifecycle method that accepts as its parameter a test function that returns anOutcome
.The
withFixture
method add by this trait has the following signature and implementation:def withFixture(test: NoArgTest): Outcome = { test() }
The
apply
method of test function interface,NoArgTest
, also returnsOutcome
:// In trait NoArgTest: def apply(): Outcome
Because the result of a test is an
Outcome
, when the test function returns, the test body must have determined an outcome already. It will already be one ofSucceeded
,Failed
,Canceled
, or Pending. This is also true whenwithFixture(NoArgTest)
returns: because the result type ofwithFixture(NoArgTest)
isOutcome
, the test has by definition already finished execution.The recommended way to ensure cleanup is performed after a test body finishes execution is to use a
try
-finally
clause. Usingtry
-finally
will ensure that cleanup will occur whether the test function completes abruptly by throwing a suite-aborting exception, or returns normally yielding anOutcome
. Note that the only situation in which a test function will complete abruptly with an exception is if the test body throws a suite-aborting exception. Any other exception will be caught and reported as either aFailed
,Canceled
, orPending
.The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. In other words, instead of writing “test()
”, you should write “super.withFixture(test)
”. Thus, the recommended structure of awithFixture
implementation that performs cleanup looks like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup here try { super.withFixture(test) // Invoke the test function } finally { // Perform cleanup here } }
If you have no cleanup to perform, you can write
withFixture
like this instead:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup here super.withFixture(test) // Invoke the test function }
If you want to perform an action only for certain outcomes, you can use a pattern match. For example, if you want to perform an action if a test fails, you'd match on
Failed
, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup here val outcome = super.withFixture(test) // Invoke the test function outcome match { case failed: Failed => // perform action that you want to occur // only if a test fails here failed case other => other } }
If you want to change the outcome in some way in
withFixture
, you can also use a pattern match. For example, if a particular exception intermittently causes a test to fail, and can transform those failures into cancelations, like this:// Your implementation override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case Failed(ex: ParticularException) => Canceled("Muting flicker", ex) case other => other } }
- trait TestSuiteMixin extends SuiteMixin
-
final
class
Tracker
extends AnyRef
Class that tracks the progress of a series of
Ordinal
s produced by invokingnext
andnextNewOldPair
on the currentOrdinal
.Class that tracks the progress of a series of
Ordinal
s produced by invokingnext
andnextNewOldPair
on the currentOrdinal
.Instances of this class are thread safe. Multiple threads can invoke
nextOrdinal
andnextTracker
concurrently. This facilitates multi-threaded tests that sendinfoProvided
reports concurrently. When using aDispatcher
to execute suites in parallel, the intention is that eachTracker
will only be used by one thread. For example, if the optionalDispatcher
passed toSuite
's implementation of runNestedSuites is defined, that method will obtain a newTracker
by invokingnextTracker
for each nested suite it passes to theDispatcher
. -
trait
TryValues
extends AnyRef
Trait that provides an implicit conversion that adds
success
andfailure
methods toscala.util.Try
, enabling you to make assertions about the value of aSuccess
or the exception of aFailure
.Trait that provides an implicit conversion that adds
success
andfailure
methods toscala.util.Try
, enabling you to make assertions about the value of aSuccess
or the exception of aFailure
.The
success
method will return theTry
on which it is invoked as aSuccess
if theTry
actually is aSuccess
, or throwTestFailedException
if not. Thefailure
method will return theTry
on which it is invoked as aFailure
if theTry
actually is aFailure
, or throwTestFailedException
if not.This construct allows you to express in one statement that an
Try
should be either aSuccess
or aFailure
and that its value or exception, respectively,should meet some expectation. Here's an example:try1.success.value should be > 9 try2.failure.exception should have message "/ by zero"
Or, using assertions instead of a matchers:
assert(try1.success.value > 9) assert(try2.failure.exception.getMessage == "/ by zero")
Were you to simply invoke
get
on theTry
, if theTry
wasn't aSuccess
, it would throw the exception contained in theFailure
:val try2 = Try { 1 / 0 } try2.get should be < 9 // try2.get throws ArithmeticException
The
ArithmeticException
would cause the test to fail, but without providing a stack depth pointing to the failing line of test code. This stack depth, provided byTestFailedException
(and a few other ScalaTest exceptions), makes it quicker for users to navigate to the cause of the failure. WithoutTryValues
, to get a stack depth exception you would need to make two statements, like this:try2 should be a 'success // throws TestFailedException try2.get should be < 9
The
TryValues
trait allows you to state that more concisely:try2.success.value should be < 9 // throws TestFailedException
-
class
WordSpec
extends WordSpecLike
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Recommended Usage: For teams coming from specs or specs2, WordSpec
will feel familiar, and is often the most natural way to port specsN tests to ScalaTest.WordSpec
is very prescriptive in how text must be written, so a good fit for teams who want a high degree of discipline enforced upon their specification text.Class
WordSpec
is so named because your specification text is structured by placing words after strings. Here's an exampleWordSpec
:package org.scalatest.examples.wordspec import org.scalatest.WordSpec class SetSpec extends WordSpec { "A Set" when { "empty" should { "have size 0" in { assert(Set.empty.size === 0) } "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
In a
WordSpec
you write a one (or more) sentence specification for each bit of behavior you wish to specify and test. Each specification sentence has a "subject," which is sometimes called the system under test (or SUT). The subject is entity being specified and tested and also serves as the subject of the sentences you write for each test. A subject can be followed by one of three verbs,should
,must
, orcan
, and a block. Here are some examples:"A Stack" should { // ... } "An Account" must { // ... } "A ShippingManifest" can { // ... }
You can describe a subject in varying situations by using a
when
clause. Awhen
clause follows the subject and precedes a block. In the block after thewhen
, you place strings that describe a situation or a state the subject may be in using a string, each followed by a verb. Here's an example:"A Stack" when { "empty" should { // ... } "non-empty" should { // ... } "full" should { // ... } }
When you are ready to finish a sentence, you write a string followed by
in
and a block that contains the code of the test. Here's an example:import org.scalatest.WordSpec class StackSpec extends WordSpec { "A Stack" when { "empty" should { "be empty" in { // ... } "complain on peek" in { // ... } "complain on pop" in { // ... } } "full" should { "be full" in { // ... } "complain on push" in { // ... } } } }
Running the above
StackSpec
in the interpreter would yield:scala> org.scalatest.run(new StackSpec) StackSpec: A Stack when empty - should be empty - should complain on peek - should complain on pop when full - should be full - should complain on push
Note that the output does not exactly match the input in an effort to maximize readability. Although the
WordSpec
code is nested, which can help you eliminate any repeated phrases in the specification portion of your code, the output printed moveswhen
andshould
down to the beginning of the next line.Sometimes you may wish to eliminate repeated phrases inside the block following a
verb
. Here's an example in which the phrase "provide an and/or operator, which" is repeated:import org.scalatest.WordSpec class AndOrSpec extends WordSpec { "The ScalaTest Matchers DSL" should { "provide an and operator, which returns silently when evaluating true and true" in {} "provide an and operator, which throws a TestFailedException when evaluating true and false" in {} "provide an and operator, which throws a TestFailedException when evaluating false and true" in {} "provide an and operator, which throws a TestFailedException when evaluating false and false" in {} "provide an or operator, which returns silently when evaluating true or true" in {} "provide an or operator, which returns silently when evaluating true or false" in {} "provide an or operator, which returns silently when evaluating false or true" in {} "provide an or operator, which throws a TestFailedException when evaluating false or false" in {} } }
In such situations you can place
which
clauses inside the verb clause, like this:import org.scalatest.WordSpec class AndOrSpec extends WordSpec { "The ScalaTest Matchers DSL" should { "provide an and operator," which { "returns silently when evaluating true and true" in {} "throws a TestFailedException when evaluating true and false" in {} "throws a TestFailedException when evaluating false and true" in {} "throws a TestFailedException when evaluating false and false" in {} } "provide an or operator," which { "returns silently when evaluating true or true" in {} "returns silently when evaluating true or false" in {} "returns silently when evaluating false or true" in {} "throws a TestFailedException when evaluating false or false" in {} } } }
Running the above
AndOrSpec
in the interpreter would yield:scala> org.scalatest.run(new AndOrSpec) AndOrSpec: The ScalaTest Matchers DSL should provide an and operator, which - returns silently when evaluating true and true - throws a TestFailedException when evaluating true and false - throws a TestFailedException when evaluating false and true - throws a TestFailedException when evaluating false and false should provide an or operator, which - returns silently when evaluating true or true - returns silently when evaluating true or false - returns silently when evaluating false or true - throws a TestFailedException when evaluating false or false
Note that unlike
when
andshould
/must
/can
, awhich
appears in the output right where you put it in the input, at the end of the line, to maximize readability.If a word or phrase is repeated at the beginning of each string contained in a block, you can eliminate that repetition by using an after word. An after word is a word or phrase that you can place after
when
, a verb, orwhich
. For example, in the previousWordSpec
, the word "provide" is repeated at the beginning of each string inside theshould
block. You can factor out this duplication like this:import org.scalatest.WordSpec class AndOrSpec extends WordSpec { def provide = afterWord("provide") "The ScalaTest Matchers DSL" should provide { "an and operator," which { "returns silently when evaluating true and true" in {} "throws a TestFailedException when evaluating true and false" in {} "that throws a TestFailedException when evaluating false and true" in {} "throws a TestFailedException when evaluating false and false" in {} } "an or operator," which { "returns silently when evaluating true or true" in {} "returns silently when evaluating true or false" in {} "returns silently when evaluating false or true" in {} "throws a TestFailedException when evaluating false or false" in {} } } }
Running the above version of
AndOrSpec
with theprovide
after word in the interpreter would give you:scala> org.scalatest.run(new AndOrSpec) AndOrSpec: The ScalaTest Matchers DSL should provide an and operator, which - returns silently when evaluating true and true - throws a TestFailedException when evaluating true and false - that throws a TestFailedException when evaluating false and true - throws a TestFailedException when evaluating false and false an or operator, which - returns silently when evaluating true or true - returns silently when evaluating true or false - returns silently when evaluating false or true - throws a TestFailedException when evaluating false or false
Once you've defined an after word, you can place it after
when
, a verb (should
,must
, orcan
), orwhich
. (You can't place one afterin
oris
, the words that introduce a test.) Here's an example that has after words used in all three places:import org.scalatest.WordSpec class ScalaTestGUISpec extends WordSpec { def theUser = afterWord("the user") def display = afterWord("display") def is = afterWord("is") "The ScalaTest GUI" when theUser { "clicks on an event report in the list box" should display { "a blue background in the clicked-on row in the list box" in {} "the details for the event in the details area" in {} "a rerun button," which is { "enabled if the clicked-on event is rerunnable" in {} "disabled if the clicked-on event is not rerunnable" in {} } } } }
Running the previous
WordSpec
in the Scala interpreter would yield:scala> org.scalatest.run(new ScalaTestGUISpec) ScalaTestGUISpec: The ScalaTest GUI when the user clicks on an event report in the list box should display - a blue background in the clicked-on row in the list box - the details for the event in the details area a rerun button, which is - enabled if the clicked-on event is rerunnable - disabled if the clicked-on event is not rerunnable
In case when you need to use different verb for a same subject, you can use
it
orthey
shorthand to avoid subject duplication:"A Stack" when { // ... } it should { // ... }
A
WordSpec
's lifecycle has two phases: the registration phase and the ready phase. It starts in registration phase and enters ready phase the first timerun
is called on it. It then remains in ready phase for the remainder of its lifetime.Tests can only be registered while the
WordSpec
is in its registration phase. Any attempt to register a test after theWordSpec
has entered its ready phase, i.e., afterrun
has been invoked on theWordSpec
, will be met with a thrownTestRegistrationClosedException
. The recommended style of usingWordSpec
is to register tests during object construction as is done in all the examples shown here. If you keep to the recommended style, you should never see aTestRegistrationClosedException
.Note: Class
WordSpec
is in part inspired by classorg.specs.Specification
, designed by Eric Torreborre for the specs framework.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,
WordSpec
adds a methodignore
to strings that can be used instead ofin
to register a test. For example, to temporarily disable the test with the name"A Stack should pop values in last-in-first-out order"
, just change “in
” into “ignore
,” like this:package org.scalatest.examples.wordspec.ignore import org.scalatest.WordSpec class SetSpec extends WordSpec { "A Set" when { "empty" should { "have size 0" ignore { assert(Set.empty.size === 0) } "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run only the second test and report that the first test was ignored:
A Set when empty - should have size 0 !!! IGNORED !!! - should should produce NoSuchElementException when head is invoked
If you wish to temporarily ignore an entire suite of tests, you can (on the JVM, not Scala.js) annotate the test class with
@Ignore
, like this:package org.scalatest.examples.wordspec.ignoreall import org.scalatest.WordSpec import org.scalatest.Ignore @Ignore class SetSpec extends WordSpec { "A Set" when { "empty" should { "have size 0" in { assert(Set.empty.size === 0) } "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
When you mark a test class with a tag annotation, ScalaTest will mark each test defined in that class with that tag. Thus, marking the
SetSpec
in the above example with the@Ignore
tag annotation means that both tests in the class will be ignored. If you run the aboveSetSpec
in the Scala interpreter, you'll see:scala> org.scalatest.run(new SetSpec) SetSpec: A Set when empty - should have size 0 !!! IGNORED !!! - should produce NoSuchElementException when head is invoked !!! IGNORED !!!
Note that marking a test class as ignored won't prevent it from being discovered by ScalaTest. Ignored classes will be discovered and run, and all their tests will be reported as ignored. This is intended to keep the ignored class visible, to encourage the developers to eventually fix and “un-ignore” it. If you want to prevent a class from being discovered at all (on the JVM, not Scala.js), use the
DoNotDiscover
annotation instead.Informers
One of the parameters to
WordSpec
'srun
method is aReporter
, 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 theReporter
as the suite runs. Most often the reporting done by default byWordSpec
's methods will be sufficient, but occasionally you may wish to provide custom information to theReporter
from a test. For this purpose, anInformer
that will forward information to the currentReporter
is provided via theinfo
parameterless method. You can pass the extra information to theInformer
via itsapply
method. TheInformer
will then pass the information to theReporter
via anInfoProvided
event.One use case for the
Informer
is to pass more information about a specification to the reporter. For example, theGivenWhenThen
trait provides methods that use the implicitinfo
provided byWordSpec
to pass such information to the reporter. Here's an example:package org.scalatest.examples.wordspec.info import collection.mutable import org.scalatest._ class SetSpec extends WordSpec with GivenWhenThen { "A mutable Set" should { "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) info("That's all folks!") } } }
If you run this
WordSpec
from the interpreter, you will see the following output:scala> org.scalatest.run(new SetSpec) A mutable Set - should allow an element to be added + Given an empty mutable Set + When an element is added + Then the Set should have size 1 + And the Set should contain the added element + That's all folks!
Documenters
WordSpec
also provides amarkup
method that returns aDocumenter
, which allows you to send to theReporter
text formatted in Markdown syntax. You can pass the extra information to theDocumenter
via itsapply
method. TheDocumenter
will then pass the information to theReporter
via anMarkupProvided
event.Here's an example
WordSpec
that usesmarkup
:package org.scalatest.examples.wordspec.markup import collection.mutable import org.scalatest._ class SetSpec extends WordSpec with GivenWhenThen { markup { """ Mutable Set ———-- A set is a collection that contains no duplicate elements. To implement a concrete mutable set, you need to provide implementations of the following methods: def contains(elem: A): Boolean def iterator: Iterator[A] def += (elem: A): this.type def -= (elem: A): this.type If you wish that methods like `take`, `drop`, `filter` return the same kind of set, you should also override: def empty: This It is also good idea to override methods `foreach` and `size` for efficiency. """ } "A mutable Set" should { "allow an element to be added" in { Given("an empty mutable Set") val set = mutable.Set.empty[String] When("an element is added") set += "clarity" Then("the Set should have size 1") assert(set.size === 1) And("the Set should contain the added element") assert(set.contains("clarity")) markup("This test finished with a **bold** statement!") } } }
Although all of ScalaTest's built-in reporters will display the markup text in some form, the HTML reporter will format the markup information into HTML. Thus, the main purpose of
markup
is to add nicely formatted text to HTML reports. Here's what the aboveSetSpec
would look like in the HTML reporter:Notifiers and alerters
ScalaTest records text passed to
info
andmarkup
during tests, and sends the recorded text in therecordedEvents
field of test completion events likeTestSucceeded
andTestFailed
. This allows string reporters (like the standard out reporter) to showinfo
andmarkup
text after the test name in a color determined by the outcome of the test. For example, if the test fails, string reporters will show theinfo
andmarkup
text in red. If a test succeeds, string reporters will show theinfo
andmarkup
text in green. While this approach helps the readability of reports, it means that you can't useinfo
to get status updates from long running tests.To get immediate (i.e., non-recorded) notifications from tests, you can use
note
(aNotifier
) andalert
(anAlerter
). Here's an example showing the differences:package org.scalatest.examples.wordspec.note import collection.mutable import org.scalatest._ class SetSpec extends WordSpec { "A mutable Set" should { "allow an element to be added" in { info("info is recorded") markup("markup is *also* recorded") note("notes are sent immediately") alert("alerts are also sent immediately") val set = mutable.Set.empty[String] set += "clarity" assert(set.size === 1) assert(set.contains("clarity")) } } }
Because
note
andalert
information is sent immediately, it will appear before the test name in string reporters, and its color will be unrelated to the ultimate outcome of the test:note
text will always appear in green,alert
text will always appear in yellow. Here's an example:scala> org.scalatest.run(new SetSpec) SetSpec: A mutable Set + notes are sent immediately + alerts are also sent immediately - should allow an element to be added + info is recorded + markup is *also* recorded
Another example is slowpoke notifications. If you find a test is taking a long time to complete, but you're not sure which test, you can enable slowpoke notifications. ScalaTest will use an
Alerter
to fire an event whenever a test has been running longer than a specified amount of time.In summary, use
info
andmarkup
for text that should form part of the specification output. Usenote
andalert
to send status notifications. (Because the HTML reporter is intended to produce a readable, printable specification,info
andmarkup
text will appear in the HTML report, butnote
andalert
text will not.)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, 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 withTestPendingException
.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 withTestPendingException
, 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. You can mark tests as pending in aWordSpec
like this:package org.scalatest.examples.wordspec.pending import org.scalatest._ class SetSpec extends WordSpec { "A Set" when { "empty" should { "have size 0" in (pending) "produce NoSuchElementException when head is invoked" in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
If you run this version of
SetSpec
with:scala> org.scalatest.run(new SetSpec)
It will run both tests but report that
should have size 0
is pending. You'll see:A Set when empty - should have size 0 (pending) - should produce NoSuchElementException when head is invoked
One difference between an ignored test and a pending one is that an ignored test is intended to be used during a significant refactorings of the code under test, when tests break and you don't want to spend the time to fix all of them immediately. You can mark some of those broken tests as ignored temporarily, so that you can focus the red bar on just failing tests you actually want to fix immediately. Later you can go back and fix the ignored tests. In other words, by ignoring some failing tests temporarily, you can more easily notice failed tests that you actually want to fix. By contrast, a pending test is intended to be used before a test and/or the code under test is written. Pending indicates you've decided to write a test for a bit of behavior, but either you haven't written the test yet, or have only written part of it, or perhaps you've written the test but don't want to implement the behavior it tests until after you've implemented a different bit of behavior you realized you need first. Thus ignored tests are designed to facilitate refactoring of existing code whereas pending tests are designed to facilitate the creation of new code.
One other difference between ignored and pending tests is that ignored tests are implemented as a test tag that is excluded by default. Thus an ignored test is never executed. By contrast, a pending test is implemented as a test that throws
TestPendingException
(which is what calling thepending
method does). Thus the body of pending tests are executed up until they throwTestPendingException
. The reason for this difference is that it enables your unfinished test to sendInfoProvided
messages to the reporter before it completes abruptly withTestPendingException
, as shown in the previous example onInformer
s that used theGivenWhenThen
trait. For example, the following snippet in aWordSpec
:"The Scala language" should { "add correctly" in { Given("two integers") When("they are added") Then("the result is the sum of the two numbers") pending } // ...
Would yield the following output when run in the interpreter:
The Scala language - should add correctly (pending) + Given two integers + When they are added + Then the result is the sum of the two numbers
Tagging tests
A
WordSpec
's tests may be classified into groups by tagging them with string names. As with any suite, when executing aWordSpec
, groups of tests can optionally be included and/or excluded. To tag aWordSpec
's tests, you pass objects that extend classorg.scalatest.Tag
to methods that register tests. ClassTag
takes one parameter, a string name. If you have created tag annotation interfaces as described in theTag
documentation, then you will probably want to use tag names on your test functions that match. To do so, simply pass the fully qualified names of the tag interfaces to theTag
constructor. For example, if you've defined a tag annotation interface with fully qualified name,com.mycompany.tags.DbTest
, then you could create a matching tag forWordSpec
s like this:package org.scalatest.examples.wordspec.tagging import org.scalatest.Tag object DbTest extends Tag("com.mycompany.tags.DbTest")
Given these definitions, you could place
WordSpec
tests into groups with tags like this:import org.scalatest.WordSpec import org.scalatest.tagobjects.Slow class SetSpec extends WordSpec { "A Set" when { "empty" should { "have size 0" taggedAs(Slow) in { assert(Set.empty.size === 0) } "produce NoSuchElementException when head is invoked" taggedAs(Slow, DbTest) in { assertThrows[NoSuchElementException] { Set.empty.head } } } } }
This code marks both tests with the
org.scalatest.tags.Slow
tag, and the second test with thecom.mycompany.tags.DbTest
tag.The
run
method takes aFilter
, whose constructor takes an optionalSet[String]
calledtagsToInclude
and aSet[String]
calledtagsToExclude
. IftagsToInclude
isNone
, all tests will be run except those those belonging to tags listed in thetagsToExclude
Set
. IftagsToInclude
is defined, only tests belonging to tags mentioned in thetagsToInclude
set, and not mentioned intagsToExclude
, will be run.It is recommended, though not required, that you create a corresponding tag annotation when you create a
Tag
object. A tag annotation (on the JVM, not Scala.js) allows you to tag all the tests of aWordSpec
in one stroke by annotating the class. For more information and examples, see the documentation for classTag
. On Scala.js, to tag all tests of a suite, you'll need to tag each test individually at the test site.Shared fixtures
A test fixture is composed of the objects and other artifacts (files, sockets, database connections, etc.) tests use to do their work. When multiple tests need to work with the same fixtures, it is important to try and avoid duplicating the fixture code across those tests. The more code duplication you have in your tests, the greater drag the tests will have on refactoring the actual production code.
ScalaTest recommends three techniques to eliminate such code duplication:
- Refactor using Scala
- Override
withFixture
- Mix in a before-and-after trait
Each technique is geared towards helping you reduce code duplication without introducing instance
var
s, shared mutable objects, or other dependencies between tests. Eliminating shared mutable state across tests will make your test code easier to reason about and more amenable for parallel test execution.The following sections describe these techniques, including explaining the recommended usage for each. But first, here's a table summarizing the options:
Refactor using Scala when different tests need different fixtures. get-fixture methods The extract method refactor helps you create a fresh instances of mutable fixture objects in each test that needs them, but doesn't help you clean them up when you're done. fixture-context objects By placing fixture methods and fields into traits, you can easily give each test just the newly created fixtures it needs by mixing together traits. Use this technique when you need different combinations of mutable fixture objects in different tests, and don't need to clean up after. loan-fixture methods Factor out dupicate code with the loan pattern when different tests need different fixtures that must be cleaned up afterwards. Override withFixture
when most or all tests need the same fixture.withFixture(NoArgTest)
The recommended default approach when most or all tests need the same fixture treatment. This general technique allows you, for example, to perform side effects at the beginning and end of all or most tests, transform the outcome of tests, retry tests, make decisions based on test names, tags, or other test data. Use this technique unless: - Different tests need different fixtures (refactor using Scala instead)
- An exception in fixture code should abort the suite, not fail the test (use a before-and-after trait instead)
- You have objects to pass into tests (override
withFixture(OneArgTest)
instead)
withFixture(OneArgTest)
Use when you want to pass the same fixture object or objects as a parameter into all or most tests. Mix in a before-and-after trait when you want an aborted suite, not a failed test, if the fixture code fails. BeforeAndAfter
Use this boilerplate-buster when you need to perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. BeforeAndAfterEach
Use when you want to stack traits that perform the same side-effects before and/or after tests, rather than at the beginning or end of tests. Calling get-fixture methods
If you need to create the same mutable fixture objects in multiple tests, and don't need to clean them up after using them, the simplest approach is to write one or more get-fixture methods. A get-fixture method returns a new instance of a needed fixture object (or an holder object containing multiple fixture objects) each time it is called. You can call a get-fixture method at the beginning of each test that needs the fixture, storing the returned object or objects in local variables. Here's an example:
package org.scalatest.examples.wordspec.getfixture import org.scalatest.WordSpec import collection.mutable.ListBuffer class ExampleSpec extends WordSpec { class Fixture { val builder = new StringBuilder("ScalaTest is ") val buffer = new ListBuffer[String] } def fixture = new Fixture "Testing" should { "be easy" in { val f = fixture f.builder.append("easy!") assert(f.builder.toString === "ScalaTest is easy!") assert(f.buffer.isEmpty) f.buffer += "sweet" } "be fun" in { val f = fixture f.builder.append("fun!") assert(f.builder.toString === "ScalaTest is fun!") assert(f.buffer.isEmpty) } } }
The “
f.
” in front of each use of a fixture object provides a visual indication of which objects are part of the fixture, but if you prefer, you can import the the members with “import f._
” and use the names directly.If you need to configure fixture objects differently in different tests, you can pass configuration into the get-fixture method. For example, you could pass in an initial value for a mutable fixture object as a parameter to the get-fixture method.
Instantiating fixture-context objects
An alternate technique that is especially useful when different tests need different combinations of fixture objects is to define the fixture objects as instance variables of fixture-context objects whose instantiation forms the body of tests. Like get-fixture methods, fixture-context objects are only appropriate if you don't need to clean up the fixtures after using them.
To use this technique, you define instance variables intialized with fixture objects in traits and/or classes, then in each test instantiate an object that contains just the fixture objects needed by the test. Traits allow you to mix together just the fixture objects needed by each test, whereas classes allow you to pass data in via a constructor to configure the fixture objects. Here's an example in which fixture objects are partitioned into two traits and each test just mixes together the traits it needs:
package org.scalatest.examples.wordspec.fixturecontext import collection.mutable.ListBuffer import org.scalatest.WordSpec class ExampleSpec extends WordSpec { trait Builder { val builder = new StringBuilder("ScalaTest is ") } trait Buffer { val buffer = ListBuffer("ScalaTest", "is") } "Testing" should { // This test needs the StringBuilder fixture "be productive" in new Builder { builder.append("productive!") assert(builder.toString === "ScalaTest is productive!") } } "Test code" should { // This test needs the ListBuffer[String] fixture "be readable" in new Buffer { buffer += ("readable!") assert(buffer === List("ScalaTest", "is", "readable!")) } // This test needs both the StringBuilder and ListBuffer "be clear and concise" in new Builder with Buffer { builder.append("clear!") buffer += ("concise!") assert(builder.toString === "ScalaTest is clear!") assert(buffer === List("ScalaTest", "is", "concise!")) } } }
Overriding
withFixture(NoArgTest)
Although the get-fixture method and fixture-context object approaches take care of setting up a fixture at the beginning of each test, they don't address the problem of cleaning up a fixture at the end of the test. If you just need to perform a side-effect at the beginning or end of a test, and don't need to actually pass any fixture objects into the test, you can override
withFixture(NoArgTest)
, one of ScalaTest's lifecycle methods defined in traitSuite
.Trait
Suite
's implementation ofrunTest
passes a no-arg test function towithFixture(NoArgTest)
. It iswithFixture
's responsibility to invoke that test function.Suite
's implementation ofwithFixture
simply invokes the function, like this:// Default implementation in trait Suite protected def withFixture(test: NoArgTest) = { test() }
You can, therefore, override
withFixture
to perform setup before and/or cleanup after invoking the test function. If you have cleanup to perform, you should invoke the test function inside atry
block and perform the cleanup in afinally
clause, in case an exception propagates back throughwithFixture
. (If a test fails because of an exception, the test function invoked by withFixture will result in aFailed
wrapping the exception. Nevertheless, best practice is to perform cleanup in a finally clause just in case an exception occurs.)The
withFixture
method is designed to be stacked, and to enable this, you should always call thesuper
implementation ofwithFixture
, and let it invoke the test function rather than invoking the test function directly. Instead of writing “test()
”, you should write “super.withFixture(test)
”, like this:// Your implementation override def withFixture(test: NoArgTest) = { // Perform setup try super.withFixture(test) // Invoke the test function finally { // Perform cleanup } }
Here's an example in which
withFixture(NoArgTest)
is used to take a snapshot of the working directory if a test fails, and send that information to the reporter:package org.scalatest.examples.wordspec.noargtest import java.io.File import org.scalatest._ class ExampleSpec extends WordSpec { override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case failed: Failed => val currDir = new File(".") val fileNames = currDir.list() info("Dir snapshot: " + fileNames.mkString(", ")) failed case other => other } } "This test" should { "succeed" in { assert(1 + 1 === 2) } "fail" in { assert(1 + 1 === 3) } } }
Running this version of
ExampleSuite
in the interpreter in a directory with two files,hello.txt
andworld.txt
would give the following output:scala> org.scalatest.run(new ExampleSuite) ExampleSuite: This test - should succeed - should fail *** FAILED *** 2 did not equal 3 (
:33) + Dir snapshot: hello.txt, world.txt Note that the
NoArgTest
passed towithFixture
, in addition to anapply
method that executes the test, also includes the test name and the config map passed torunTest
. Thus you can also use the test name and configuration objects in yourwithFixture
implementation.Calling loan-fixture methods
If you need to both pass a fixture object into a test and perform cleanup at the end of the test, you'll need to use the loan pattern. If different tests need different fixtures that require cleanup, you can implement the loan pattern directly by writing loan-fixture methods. A loan-fixture method takes a function whose body forms part or all of a test's code. It creates a fixture, passes it to the test code by invoking the function, then cleans up the fixture after the function returns.
The following example shows three tests that use two fixtures, a database and a file. Both require cleanup after, so each is provided via a loan-fixture method. (In this example, the database is simulated with a
StringBuffer
.)import java.util.concurrent.ConcurrentHashMap object DbServer { // Simulating a database server type Db = StringBuffer private val databases = new ConcurrentHashMap[String, Db] def createDb(name: String): Db = { val db = new StringBuffer databases.put(name, db) db } def removeDb(name: String) { databases.remove(name) } } import org.scalatest.WordSpec import DbServer._ import java.util.UUID.randomUUID import java.io._ class ExampleSpec extends WordSpec { def withDatabase(testCode: Db => Any) { val dbName = randomUUID.toString val db = createDb(dbName) // create the fixture try { db.append("ScalaTest is ") // perform setup testCode(db) // "loan" the fixture to the test } finally removeDb(dbName) // clean up the fixture } def withFile(testCode: (File, FileWriter) => Any) { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) try { writer.write("ScalaTest is ") // set up the fixture testCode(file, writer) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } "Testing" should { // This test needs the file fixture "be productive" in withFile { (file, writer) => writer.write("productive!") writer.flush() assert(file.length === 24) } } "Test code" should { // This test needs the database fixture "be readable" in withDatabase { db => db.append("readable!") assert(db.toString === "ScalaTest is readable!") } // This test needs both the file and the database "be clear and concise" in withDatabase { db => withFile { (file, writer) => // loan-fixture methods compose db.append("clear!") writer.write("concise!") writer.flush() assert(db.toString === "ScalaTest is clear!") assert(file.length === 21) } } } }
As demonstrated by the last test, loan-fixture methods compose. Not only do loan-fixture methods allow you to give each test the fixture it needs, they allow you to give a test multiple fixtures and clean everything up afterwards.
Also demonstrated in this example is the technique of giving each test its own "fixture sandbox" to play in. When your fixtures involve external side-effects, like creating files or databases, it is a good idea to give each file or database a unique name as is done in this example. This keeps tests completely isolated, allowing you to run them in parallel if desired.
Overriding
withFixture(OneArgTest)
If all or most tests need the same fixture, you can avoid some of the boilerplate of the loan-fixture method approach by using a
fixture.WordSpec
and overridingwithFixture(OneArgTest)
. Each test in afixture.WordSpec
takes a fixture as a parameter, allowing you to pass the fixture into the test. You must indicate the type of the fixture parameter by specifyingFixtureParam
, and implement awithFixture
method that takes aOneArgTest
. ThiswithFixture
method is responsible for invoking the one-arg test function, so you can perform fixture set up before, and clean up after, invoking and passing the fixture into the test function.To enable the stacking of traits that define
withFixture(NoArgTest)
, it is a good idea to letwithFixture(NoArgTest)
invoke the test function instead of invoking the test function directly. To do so, you'll need to convert theOneArgTest
to aNoArgTest
. You can do that by passing the fixture object to thetoNoArgTest
method ofOneArgTest
. Instead of writing “test(theFixture)
”, you'd delegate responsibility for invoking the test function to thewithFixture(NoArgTest)
method of the same instance by writing:withFixture(test.toNoArgTest(theFixture))
Here's a complete example:
package org.scalatest.examples.wordspec.oneargtest import org.scalatest.fixture import java.io._ class ExampleSpec extends fixture.WordSpec { case class FixtureParam(file: File, writer: FileWriter) def withFixture(test: OneArgTest) = { val file = File.createTempFile("hello", "world") // create the fixture val writer = new FileWriter(file) val theFixture = FixtureParam(file, writer) try { writer.write("ScalaTest is ") // set up the fixture withFixture(test.toNoArgTest(theFixture)) // "loan" the fixture to the test } finally writer.close() // clean up the fixture } "Testing" should { "be easy" in { f => f.writer.write("easy!") f.writer.flush() assert(f.file.length === 18) } "be fun" in { f => f.writer.write("fun!") f.writer.flush() assert(f.file.length === 17) } } }
In this example, the tests actually required two fixture objects, a
File
and aFileWriter
. In such situations you can simply define theFixtureParam
type to be a tuple containing the objects, or as is done in this example, a case class containing the objects. For more information on thewithFixture(OneArgTest)
technique, see the documentation forfixture.WordSpec
.Mixing in
BeforeAndAfter
In all the shared fixture examples shown so far, the activities of creating, setting up, and cleaning up the fixture objects have been performed during the test. This means that if an exception occurs during any of these activities, it will be reported as a test failure. Sometimes, however, you may want setup to happen before the test starts, and cleanup after the test has completed, so that if an exception occurs during setup or cleanup, the entire suite aborts and no more tests are attempted. The simplest way to accomplish this in ScalaTest is to mix in trait
BeforeAndAfter
. With this trait you can denote a bit of code to run before each test withbefore
and/or after each test each test withafter
, like this:package org.scalatest.examples.wordspec.beforeandafter import org.scalatest.WordSpec import org.scalatest.BeforeAndAfter import collection.mutable.ListBuffer class ExampleSpec extends WordSpec with BeforeAndAfter { val builder = new StringBuilder val buffer = new ListBuffer[String] before { builder.append("ScalaTest is ") } after { builder.clear() buffer.clear() } "Testing" should { "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) } } }
Note that the only way
before
andafter
code can communicate with test code is via some side-effecting mechanism, commonly by reassigning instancevar
s or by changing the state of mutable objects held from instanceval
s (as in this example). If using instancevar
s or mutable objects held from instanceval
s you wouldn't be able to run tests in parallel in the same instance of the test class (on the JVM, not Scala.js) unless you synchronized access to the shared, mutable state. This is why ScalaTest'sParallelTestExecution
trait extendsOneInstancePerTest
. By running each test in its own instance of the class, each test has its own copy of the instance variables, so you don't need to synchronize. If you mixedParallelTestExecution
into theExampleSuite
above, the tests would run in parallel just fine without any synchronization needed on the mutableStringBuilder
andListBuffer[String]
objects.Although
BeforeAndAfter
provides a minimal-boilerplate way to execute code before and after tests, it isn't designed to enable stackable traits, because the order of execution would be non-obvious. If you want to factor out before and after code that is common to multiple test suites, you should use traitBeforeAndAfterEach
instead, as shown later in the next section, composing fixtures by stacking traits.Composing fixtures by stacking traits
In larger projects, teams often end up with several different fixtures that test classes need in different combinations, and possibly initialized (and cleaned up) in different orders. A good way to accomplish this in ScalaTest is to factor the individual fixtures into traits that can be composed using the stackable trait pattern. This can be done, for example, by placing
withFixture
methods in several traits, each of which callsuper.withFixture
. Here's an example in which theStringBuilder
andListBuffer[String]
fixtures used in the previous examples have been factored out into two stackable fixture traits namedBuilder
andBuffer
:package org.scalatest.examples.wordspec.composingwithfixture import org.scalatest._ import collection.mutable.ListBuffer trait Builder extends TestSuiteMixin { this: TestSuite => val builder = new StringBuilder abstract override def withFixture(test: NoArgTest) = { builder.append("ScalaTest is ") try super.withFixture(test) // To be stackable, must call super.withFixture finally builder.clear() } } trait Buffer extends TestSuiteMixin { this: TestSuite => val buffer = new ListBuffer[String] abstract override def withFixture(test: NoArgTest) = { try super.withFixture(test) // To be stackable, must call super.withFixture finally buffer.clear() } } class ExampleSpec extends WordSpec with Builder with Buffer { "Testing" should { "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
By mixing in both the
Builder
andBuffer
traits,ExampleSpec
gets both fixtures, which will be initialized before each test and cleaned up after. The order the traits are mixed together determines the order of execution. In this case,Builder
is “super” toBuffer
. If you wantedBuffer
to be “super” toBuilder
, you need only switch the order you mix them together, like this:class Example2Spec extends WordSpec with Buffer with Builder
And if you only need one fixture you mix in only that trait:
class Example3Spec extends WordSpec with Builder
Another way to create stackable fixture traits is by extending the
BeforeAndAfterEach
and/orBeforeAndAfterAll
traits.BeforeAndAfterEach
has abeforeEach
method that will be run before each test (like JUnit'ssetUp
), and anafterEach
method that will be run after (like JUnit'stearDown
). Similarly,BeforeAndAfterAll
has abeforeAll
method that will be run before all tests, and anafterAll
method that will be run after all tests. Here's what the previously shown example would look like if it were rewritten to use theBeforeAndAfterEach
methods instead ofwithFixture
:package org.scalatest.examples.wordspec.composingbeforeandaftereach import org.scalatest._ import org.scalatest.BeforeAndAfterEach import collection.mutable.ListBuffer trait Builder extends BeforeAndAfterEach { this: Suite => val builder = new StringBuilder override def beforeEach() { builder.append("ScalaTest is ") super.beforeEach() // To be stackable, must call super.beforeEach } override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally builder.clear() } } trait Buffer extends BeforeAndAfterEach { this: Suite => val buffer = new ListBuffer[String] override def afterEach() { try super.afterEach() // To be stackable, must call super.afterEach finally buffer.clear() } } class ExampleSpec extends WordSpec with Builder with Buffer { "Testing" should { "be easy" in { builder.append("easy!") assert(builder.toString === "ScalaTest is easy!") assert(buffer.isEmpty) buffer += "sweet" } "be fun" in { builder.append("fun!") assert(builder.toString === "ScalaTest is fun!") assert(buffer.isEmpty) buffer += "clear" } } }
To get the same ordering as
withFixture
, place yoursuper.beforeEach
call at the end of eachbeforeEach
method, and thesuper.afterEach
call at the beginning of eachafterEach
method, as shown in the previous example. It is a good idea to invokesuper.afterEach
in atry
block and perform cleanup in afinally
clause, as shown in the previous example, because this ensures the cleanup code is performed even ifsuper.afterEach
throws an exception.The difference between stacking traits that extend
BeforeAndAfterEach
versus traits that implementwithFixture
is that setup and cleanup code happens before and after the test inBeforeAndAfterEach
, but at the beginning and end of the test inwithFixture
. Thus if awithFixture
method completes abruptly with an exception, it is considered a failed test. By contrast, if any of thebeforeEach
orafterEach
methods ofBeforeAndAfterEach
complete abruptly, it is considered an aborted suite, which will result in aSuiteAborted
event.Shared tests
Sometimes you may want to run the same test code on different fixture objects. In other words, you may want to write tests that are "shared" by different fixture objects. To accomplish this in a
WordSpec
, you first place shared tests in behavior functions. These behavior functions will be invoked during the construction phase of anyWordSpec
that uses them, so that the tests they contain will be registered as tests in thatWordSpec
. For example, given this stack class:import scala.collection.mutable.ListBuffer class Stack[T] { val MAX = 10 private val buf = new ListBuffer[T] def push(o: T) { if (!full) buf.prepend(o) else throw new IllegalStateException("can't push onto a full stack") } def pop(): T = { if (!empty) buf.remove(0) else throw new IllegalStateException("can't pop an empty stack") } def peek: T = { if (!empty) buf(0) else throw new IllegalStateException("can't pop an empty stack") } def full: Boolean = buf.size == MAX def empty: Boolean = buf.size == 0 def size = buf.size override def toString = buf.mkString("Stack(", ", ", ")") }
You may want to test the
Stack
class in different states: empty, full, with one item, with one item less than capacity, etc. You may find you have several tests that make sense any time the stack is non-empty. Thus you'd ideally want to run those same tests for three stack fixture objects: a full stack, a stack with a one item, and a stack with one item less than capacity. With shared tests, you can factor these tests out into a behavior function, into which you pass the stack fixture to use when running the tests. So in yourWordSpec
for stack, you'd invoke the behavior function three times, passing in each of the three stack fixtures so that the shared tests are run for all three fixtures. You can define a behavior function that encapsulates these shared tests inside theWordSpec
that uses them. If they are shared between differentWordSpec
s, however, you could also define them in a separate trait that is mixed into eachWordSpec
that uses them.For example, here the
nonEmptyStack
behavior function (in this case, a behavior method) is defined in a trait along with another method containing shared tests for non-full stacks:trait StackBehaviors { this: WordSpec => def nonEmptyStack(newStack: => Stack[Int], lastItemAdded: Int) { "be non-empty" in { assert(!newStack.empty) } "return the top item on peek" in { assert(newStack.peek === lastItemAdded) } "not remove the top item on peek" in { val stack = newStack val size = stack.size assert(stack.peek === lastItemAdded) assert(stack.size === size) } "remove the top item on pop" in { val stack = newStack val size = stack.size assert(stack.pop === lastItemAdded) assert(stack.size === size - 1) } } def nonFullStack(newStack: => Stack[Int]) { "not be full" in { assert(!newStack.full) } "add to the top on push" in { val stack = newStack val size = stack.size stack.push(7) assert(stack.size === size + 1) assert(stack.peek === 7) } } }
Given these behavior functions, you could invoke them directly, but
WordSpec
offers a DSL for the purpose, which looks like this:behave like nonEmptyStack(stackWithOneItem, lastValuePushed) behave like nonFullStack(stackWithOneItem)
If you prefer to use an imperative style to change fixtures, for example by mixing in
BeforeAndAfterEach
and reassigning astack
var
inbeforeEach
, you could write your behavior functions in the context of thatvar
, which means you wouldn't need to pass in the stack fixture because it would be in scope already inside the behavior function. In that case, your code would look like this:behave like nonEmptyStack // assuming lastValuePushed is also in scope inside nonEmptyStack behave like nonFullStack
The recommended style, however, is the functional, pass-all-the-needed-values-in style. Here's an example:
class SharedTestExampleSpec extends WordSpec with StackBehaviors { // Stack fixture creation methods def emptyStack = new Stack[Int] def fullStack = { val stack = new Stack[Int] for (i <- 0 until stack.MAX) stack.push(i) stack } def stackWithOneItem = { val stack = new Stack[Int] stack.push(9) stack } def stackWithOneItemLessThanCapacity = { val stack = new Stack[Int] for (i <- 1 to 9) stack.push(i) stack } val lastValuePushed = 9 "A Stack" when { "empty" should { "be empty" in { assert(emptyStack.empty) } "complain on peek" in { assertThrows[IllegalStateException] { emptyStack.peek } } "complain on pop" in { assertThrows[IllegalStateException] { emptyStack.pop } } } "it contains one item" should { behave like nonEmptyStack(stackWithOneItem, lastValuePushed) behave like nonFullStack(stackWithOneItem) } "it contains one item less than capacity" should { behave like nonEmptyStack(stackWithOneItemLessThanCapacity, lastValuePushed) behave like nonFullStack(stackWithOneItemLessThanCapacity) } "full" should { "be full" in { assert(fullStack.full) } behave like nonEmptyStack(fullStack, lastValuePushed) "complain on a push" in { assertThrows[IllegalStateException] { fullStack.push(10) } } } } }
If you load these classes into the Scala interpreter (with scalatest's JAR file on the class path), and execute it, you'll see:
scala> org.scalatest.run(new SharedTestExampleSpec) SharedTestExampleSpec: A Stack when empty - should be empty - should complain on peek - should complain on pop when it contains one item - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push when it contains one item less than capacity - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should not be full - should add to the top on push when full - should be full - should be non-empty - should return the top item on peek - should not remove the top item on peek - should remove the top item on pop - should complain on a push
One thing to keep in mind when using shared tests is that in ScalaTest, each test in a suite must have a unique name. If you register the same tests repeatedly in the same suite, one problem you may encounter is an exception at runtime complaining that multiple tests are being registered with the same test name. A good way to solve this problem in a
WordSpec
is to make sure each invocation of a behavior function is in the context of a different surroundingwhen
,should
/must
/can
, orwhich
clause, because a test's name is the concatenation of its surrounding clauses and after words, followed by the "spec text". For example, the following code in aWordSpec
would register a test with the name"A Stack when empty should be empty"
:"A Stack" when { "empty" should { "be empty" in { assert(emptyStack.empty) } } } // ...
If the
"be empty"
test was factored out into a behavior function, it could be called repeatedly so long as each invocation of the behavior function is in the context of a different surroundingwhen
clauses. -
trait
WordSpecLike
extends TestSuite with TestRegistration with ShouldVerb with MustVerb with CanVerb with Informing with Notifying with Alerting with Documenting
Implementation trait for class
WordSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.Implementation trait for class
WordSpec
, which facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.WordSpec
is a class, not a trait, to minimize compile time given there is a slight compiler overhead to mixing in traits compared to extending classes. If you need to mix the behavior ofWordSpec
into some other class, you can use this trait instead, because classWordSpec
does nothing more than extend this trait and add a nicetoString
implementation.See the documentation of the class for a detailed overview of
WordSpec
. -
trait
WrapWith
extends Annotation
Annotation to associate a wrapper suite with a non-
Suite
class, so it can be run via ScalaTest.Annotation to associate a wrapper suite with a non-
Suite
class, so it can be run via ScalaTest.Note: This is actually an annotation defined in Java, not a Scala trait. It must be defined in Java instead of Scala so it will be accessible at runtime. It has been inserted into Scaladoc by pretending it is a trait.
A class will be considered annotated with
WrapWith
if it is annotated directly or one of its superclasses (but not supertraits) are annotated withWrapWith
. The wrapper suite must have a public, one-arg constructor that takes aClass
instance whose type parameter is compatible with the class to wrap: i.e., the class being annotated withWrapWith
. ScalaTest will load the class to wrap and construct a new instance of the wrapper suite, passing in theClass
instance for the class to wrap. Here's an example:import org.scalacheck.Properties @WrapWith(classOf[ScalaCheckPropertiesSpec]) class StringSpecification extends Properties("String") { // ... }
The
ScalaCheckPropertiesSpec
would need to have a public, no-arg constructor that accepts subclasses oforg.scalacheck.Properties
:import org.scalacheck.Properties import org.scalatest.Suite class ScalaCheckPropertiesSpec(clazz: Class[_ <: Properties]) extends Suite { // ... }
-
type
PendingNothing = PendingStatement
- Annotations
- @deprecated
- Deprecated
Please use PendingStatement instead
-
trait
Rerunner
extends AnyRef
Trait whose instances can rerun tests or other entities (such as suites).
Trait whose instances can rerun tests or other entities (such as suites). An object extending this trait can be passed to a
Reporter
as part of aReport
. The test or other entity about which the report is made can then be rerun by invoking thererun
method on theRerunnable
.- Annotations
- @deprecated
- Deprecated
We are considering removing Rerunner in ScalaTest 2.0 and would like to know if anyone is using it. If you are, please email scalatest-users@googlegroups.com or and describe your use case. Thanks!
-
type
Spec = RefSpec
The name
org.scalatest.Spec
has been deprecated and will be removed in a future version of ScalaTest. Please use its new name,org.scalatest.refspec.RefSpec
, instead.The name
org.scalatest.Spec
has been deprecated and will be removed in a future version of ScalaTest. Please use its new name,org.scalatest.refspec.RefSpec
, instead.Because this style uses reflection at runtime to discover scopes and tests, it can only be supported on the JVM, not Scala.js. Thus in ScalaTest 3.0.0, class
org.scalatest.Spec
was moved to theorg.scalatest.refspec
package and renamedRefSpec
, with the intention of later moving it to a separate module available only on the JVM.- Annotations
- @deprecated
- Deprecated
Please use org.scalatest.refspec.RefSpec instead
-
type
SpecLike = RefSpecLike
The name
org.scalatest.SpecLike
has been deprecated and will be removed in a future version of ScalaTest. Please use its new name,org.scalatest.refspec.RefSpecLike
, instead.The name
org.scalatest.SpecLike
has been deprecated and will be removed in a future version of ScalaTest. Please use its new name,org.scalatest.refspec.RefSpecLike
, instead.Because this style uses reflection at runtime to discover scopes and tests, it can only be supported on the JVM, not Scala.js. Thus in ScalaTest 3.0.0, class
org.scalatest.SpecLike
was moved to theorg.scalatest.refspec
package and renamedRefSpecLike
, with the intention of later moving it to a separate module available only on the JVM.- Annotations
- @deprecated
- Deprecated
Please use org.scalatest.refspec.RefSpecLike instead
Value Members
-
val
ScalaTestVersion: String
The version number of ScalaTest.
The version number of ScalaTest.
- returns
the ScalaTest version number.
-
lazy val
color: Shell
Returns a copy of this
Shell
withcolorPassed
configuration parameter set totrue
.Returns a copy of this
Shell
withcolorPassed
configuration parameter set totrue
. -
lazy val
durations: Shell
Returns a copy of this
Shell
withdurationsPassed
configuration parameter set totrue
.Returns a copy of this
Shell
withdurationsPassed
configuration parameter set totrue
. -
lazy val
fullstacks: Shell
Returns a copy of this
Shell
withfullStacksPassed
configuration parameter set totrue
.Returns a copy of this
Shell
withfullStacksPassed
configuration parameter set totrue
. -
lazy val
nocolor: Shell
Returns a copy of this
Shell
withcolorPassed
configuration parameter set tofalse
.Returns a copy of this
Shell
withcolorPassed
configuration parameter set tofalse
. -
lazy val
nodurations: Shell
Returns a copy of this
Shell
withdurationsPassed
configuration parameter set tofalse
.Returns a copy of this
Shell
withdurationsPassed
configuration parameter set tofalse
. -
lazy val
nostacks: Shell
Returns a copy of this
Shell
withshortStacksPassed
configuration parameter set tofalse
.Returns a copy of this
Shell
withshortStacksPassed
configuration parameter set tofalse
. -
lazy val
nostats: Shell
Returns a copy of this
Shell
withstatsPassed
configuration parameter set tofalse
.Returns a copy of this
Shell
withstatsPassed
configuration parameter set tofalse
. -
lazy val
shortstacks: Shell
Returns a copy of this
Shell
withshortStacksPassed
configuration parameter set totrue
.Returns a copy of this
Shell
withshortStacksPassed
configuration parameter set totrue
. -
lazy val
stats: Shell
Returns a copy of this
Shell
withstatsPassed
configuration parameter set totrue
.Returns a copy of this
Shell
withstatsPassed
configuration parameter set totrue
. -
object
AppendedClues
extends AppendedClues
Companion object that facilitates the importing of
AppendedClues
members as an alternative to mixing it in.Companion object that facilitates the importing of
AppendedClues
members as an alternative to mixing it in. One use case is to importAppendedClues
members so you can use them in the Scala interpreter. -
object
Assertions
extends Assertions
Companion object that facilitates the importing of
Assertions
members as an alternative to mixing it in.Companion object that facilitates the importing of
Assertions
members as an alternative to mixing it in. One use case is to importAssertions
members so you can use them in the Scala interpreter:$scala -classpath scalatest.jar Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java 1.5.0_16). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest.Assertions._ import org.scalatest.Assertions._ scala> assert(1 === 2) org.scalatest.TestFailedException: 1 did not equal 2 at org.scalatest.Assertions$class.assert(Assertions.scala:211) at org.scalatest.Assertions$.assert(Assertions.scala:511) at .<init>(<console>:7) at .<clinit>(<console>) at RequestResult$.<init>(<console>:3) at RequestResult$.<clinit>(<console>) at RequestResult$result(<console>) at sun.reflect.NativeMethodAccessorImpl.invoke... scala> assertResult(3) { 1 + 3 } org.scalatest.TestFailedException: Expected 3, but got 4 at org.scalatest.Assertions$class.expect(Assertions.scala:447) at org.scalatest.Assertions$.expect(Assertions.scala:511) at .<init>(<console>:7) at .<clinit>(<console>) at RequestResult$.<init>(<console>:3) at RequestResult$.<clinit>(<console>) at RequestResult$result(<console>) at sun.reflect.NativeMethodAccessorImpl.in... scala> val caught = intercept[StringIndexOutOfBoundsException] { "hi".charAt(-1) } caught: StringIndexOutOfBoundsException = java.lang.StringIndexOutOfBoundsException: String index out of range: -1
-
object
Canceled
extends Serializable
Companion object to class
Canceled
that provides, in addition to the extractor and factory method provided by the compiler given its companion is a case class, a second factory method that produces aCanceled
outcome given a string message.Companion object to class
Canceled
that provides, in addition to the extractor and factory method provided by the compiler given its companion is a case class, a second factory method that produces aCanceled
outcome given a string message. -
object
Checkpoints
extends Checkpoints
Companion object that facilitates the importing the members of trait
Checkpoints
as an alternative to mixing it in.Companion object that facilitates the importing the members of trait
Checkpoints
as an alternative to mixing it in. One use case is to importCheckpoints
so you can use it in the Scala interpreter. -
object
CompleteLastly
extends CompleteLastly
Companion object that facilitates the importing of
CompleteLastly
members as an alternative to mixing it in.Companion object that facilitates the importing of
CompleteLastly
members as an alternative to mixing it in. -
object
ConfigMap
extends Serializable
Companion object to class
ConfigMap
containing factory methods.Companion object to class
ConfigMap
containing factory methods. -
object
DiagrammedAssertions
extends DiagrammedAssertions
Companion object that facilitates the importing of
DiagrammedAssertions
members as an alternative to mixing it in.Companion object that facilitates the importing of
DiagrammedAssertions
members as an alternative to mixing it in. One use case is to importDiagrammedAssertions
members so you can use them in the Scala interpreter:$scala -classpath scalatest.jar Welcome to Scala version 2.10.4.final (Java HotSpot(TM) Client VM, Java 1.6.0_45). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest.Assertions._ import org.scalatest.Assertions._ scala> assert(1 === 2) org.scalatest.exceptions.TestFailedException: assert(1 === 2) | | | 1 | 2 false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) at org.scalatest.DiagrammedAssertions$.newAssertionFailedException(DiagrammedAssertions.scala:249) at org.scalatest.DiagrammedAssertions$DiagrammedAssertionsHelper.macroAssert(DiagrammedAssertions.scala:111) at .<init>(<console>:20) at .<clinit>(<console>) at .<init>(<console>:7) at .<clinit>(<console>) at $print(<console>) at sun.reflect.NativeMethodAccessorImpl.invoke...
-
object
DiagrammedExpr
DiagrammedExpr
companion object that provides factory methods to create different sub types ofDiagrammedExpr
DiagrammedExpr
companion object that provides factory methods to create different sub types ofDiagrammedExpr
DiagrammedExpr
is used by code generated fromDiagrammedAssertionsMacro
, it needs to be public so that the generated code can be compiled. It is expected that ScalaTest users would ever need to useDiagrammedExpr
directly. -
object
EitherValues
extends EitherValues
Companion object that facilitates the importing of
ValueEither
members as an alternative to mixing it in.Companion object that facilitates the importing of
ValueEither
members as an alternative to mixing it in. One use case is to importEitherValues
's members so you can useleft.value
andright.value
onEither
in the Scala interpreter:$ scala -cp scalatest-1.7.jar Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest._ import org.scalatest._ scala> import matchers.Matchers._ import matchers.Matchers._ scala> import EitherValues._ import EitherValues._ scala> val e: Either[String, Int] = Left("Muchas problemas") e: Either[String,Int] = Left(Muchas problemas) scala> e.left.value should be ("Muchas problemas") scala> e.right.value should be < 9 org.scalatest.TestFailedException: The Either on which rightValue was invoked was not defined. at org.scalatest.EitherValues$RightValuable.value(EitherValues.scala:148) at .<init>(<console>:18) ...
-
object
Exceptional
extends Serializable
Companion object to class
Exceptional
that provides a factory method and an extractor that enables patterns that match bothFailed
andCanceled
outcomes and extracts the contained exception and a factory method.Companion object to class
Exceptional
that provides a factory method and an extractor that enables patterns that match bothFailed
andCanceled
outcomes and extracts the contained exception and a factory method. - object Failed extends Serializable
-
object
FailedStatus
extends Status with Serializable
Singleton status that represents an already completed run with at least one failed test or aborted suite.
Singleton status that represents an already completed run with at least one failed test or aborted suite.
Note: the difference between this
FailedStatus
object and the similarly namedFailed
class is that aFailed
instance indicates one test failed, whereas thisFailedStatus
object indicates either one or more tests failed and/or one or more suites aborted during a run. Both are used as the result type ofSuite
lifecycle methods, butFailed
is a possible result ofwithFixture
, whereasFailedStatus
is a possible result ofrun
,runNestedSuites
,runTests
, orrunTest
. In short,Failed
is always just about one test, whereasFailedStatus
could be about something larger: multiple tests or an entire suite. - object Filter extends Serializable
-
object
FutureOutcome
Companion object to
FutureOutcomes
that contains factory methods for creating already-completedFutureOutcomes
.Companion object to
FutureOutcomes
that contains factory methods for creating already-completedFutureOutcomes
. -
object
Inside
extends Inside
Companion object that facilitates the importing of the
inside
construct as an alternative to mixing it in.Companion object that facilitates the importing of the
inside
construct as an alternative to mixing it in. One use case is to import theinside
construct so you can use it in the Scala interpreter:$ scala -cp scalatest-1.8.jar Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest._ import org.scalatest._ scala> import matchers.Matchers._ import matchers.Matchers._ scala> import Inside._ import Inside._ scala> inside (List(1, 2, 3)) { case List(x, y, z) => | y should equal (2) | } scala> inside (List(1, 2, 3)) { case List(x, y, z) => | x should equal (2) | } org.scalatest.TestFailedException: 1 did not equal 2, inside List(1, 2, 3) at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:150) at org.scalatest.matchers.Matchers$.newTestFailedException(Matchers.scala:2331) at org.scalatest.matchers.Matchers$ShouldMethodHelper$.shouldMatcher(Matchers.scala:873) ...
-
object
Inspectors
extends Inspectors
Companion object that facilitates the importing of
Inspectors
members as an alternative to mixing it in.Companion object that facilitates the importing of
Inspectors
members as an alternative to mixing it in. One use case is to importInspectors
's members so you can use them in the Scala interpreter. -
object
LoneElement
extends LoneElement
Companion object that facilitates the importing of
LoneElement
members as an alternative to mixing it in.Companion object that facilitates the importing of
LoneElement
members as an alternative to mixing it in. One use case is to importLoneElement
's members so you can useloneElement
in the Scala interpreter. -
object
Matchers
extends Matchers
Companion object that facilitates the importing of
Matchers
members as an alternative to mixing it the trait.Companion object that facilitates the importing of
Matchers
members as an alternative to mixing it the trait. One use case is to importMatchers
members so you can use them in the Scala interpreter. -
object
MustMatchers
extends MustMatchers
Companion object that facilitates the importing of
Matchers
members as an alternative to mixing it the trait.Companion object that facilitates the importing of
Matchers
members as an alternative to mixing it the trait. One use case is to importMatchers
members so you can use them in the Scala interpreter. -
object
NonImplicitAssertions
extends NonImplicitAssertions
Companion object that facilitates the importing of the members of trait
Assertions
without importing the implicit conversions it provides by default.Companion object that facilitates the importing of the members of trait
Assertions
without importing the implicit conversions it provides by default. One use case for this object is to import the non-implicitAssertions
members so you can use them in the Scala interpreter along with another library whose implicits conflict with those provided byAssertions
:$ scala -cp scalatest-1.7.jar Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest._ import org.scalatest._ scala> import NonImplicitAssertions._ import NonImplicitAssertions._ scala> assert(1 + 1 === 2) <console>:14: error: value === is not a member of Int assert(1 + 1 === 2) ^ scala> assert(1 + 1 == 2) scala> expect(2) { 1 + 1 } scala> expect(2) { 1 + 1 + 1 } org.scalatest.TestFailedException: Expected 2, but got 3 at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:318) at org.scalatest.NonImplicitAssertions$.newAssertionFailedException(NonImplicitAssertions.scala:73) ... scala> intercept[IndexOutOfBoundsException] { "hi".charAt(-1) } res3: IndexOutOfBoundsException = java.lang.StringIndexOutOfBoundsException: String index out of range: -1
-
object
OptionValues
extends OptionValues
Companion object that facilitates the importing of
OptionValues
members as an alternative to mixing it in.Companion object that facilitates the importing of
OptionValues
members as an alternative to mixing it in. One use case is to importOptionValues
's members so you can usevalue
on option in the Scala interpreter:$ scala -cp scalatest-1.7.jar Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest._ import org.scalatest._ scala> import matchers.Matchers._ import matchers.Matchers._ scala> import OptionValues._ import OptionValues._ scala> val opt1: Option[Int] = Some(1) opt1: Option[Int] = Some(1) scala> val opt2: Option[Int] = None opt2: Option[Int] = None scala> opt1.value should be < 10 scala> opt2.value should be < 10 org.scalatest.TestFailedException: The Option on which value was invoked was not defined. at org.scalatest.OptionValues$Valuable.value(OptionValues.scala:68) at .<init>(<console>:18) ...
-
object
Outcome
extends Serializable
Companion object for trait
Outcome
that contains an implicit method that enables collections ofOutcome
s to be flattened into a collections of contained exceptions.Companion object for trait
Outcome
that contains an implicit method that enables collections ofOutcome
s to be flattened into a collections of contained exceptions. -
object
OutcomeOf
extends OutcomeOf
Companion object that facilitates the importing of
OutcomeOf
's method as an alternative to mixing it in.Companion object that facilitates the importing of
OutcomeOf
's method as an alternative to mixing it in. One use case is to importOutcomeOf
's method so you can use it in the Scala interpreter. -
object
PartialFunctionValues
extends PartialFunctionValues
Companion object that facilitates the importing of
PartialFunctionValues
members as an alternative to mixing it in.Companion object that facilitates the importing of
PartialFunctionValues
members as an alternative to mixing it in. One use case is to importPartialFunctionValues
's members so you can use thevalueAt
method onPartialFunction
in the Scala interpreter:$ scala -cp scalatest-1.7.jar Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest._ import org.scalatest._ scala> import matchers.Matchers._ import matchers.Matchers._ scala> import PartialFunctionValues._ import PartialFunctionValues._ scala> val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4) pf: PartialFunction[String,Int] = Map(I -> 1, II -> 2, III -> 3, IV -> 4) scala> pf("IV") should equal (4) scala> pf("V") should equal (5) java.util.NoSuchElementException: key not found: V at scala.collection.MapLike$class.default(MapLike.scala:224) at scala.collection.immutable.Map$Map4.default(Map.scala:167) ...
-
object
Payloads
extends Payloads
Companion object that facilitates the importing of
Payloads
members as an alternative to mixing it in.Companion object that facilitates the importing of
Payloads
members as an alternative to mixing it in. One use case is to importPayloads
members so you can use them in the Scala interpreter. -
object
Pending
extends Outcome with Product with Serializable
Outcome for a test that was pending, which contains an optional string giving more information on what exactly is needed for the test to become non-pending.
-
object
PrivateMethodTester
extends PrivateMethodTester
Companion object that facilitates the importing of
PrivateMethodTester
members as an alternative to mixing it in.Companion object that facilitates the importing of
PrivateMethodTester
members as an alternative to mixing it in. One use case is to importPrivateMethodTester
members so you can use them in the Scala interpreter:$scala -classpath scalatest.jar Welcome to Scala version 2.7.5.final (Java HotSpot(TM) Client VM, Java 1.5.0_16). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalatest.PrivateMethodTester._ import org.scalatest.PrivateMethodTester._ scala> class Example { | private def addSesame(prefix: String) = prefix + " sesame" | } defined class Example scala> val example = new Example example: Example = Example@d8b6fe scala> val addSesame = PrivateMethod[String]('addSesame) addSesame: org.scalatest.PrivateMethodTester.PrivateMethod[String] = org.scalatest.PrivateMethodTester$PrivateMethod@5cdf95 scala> example invokePrivate addSesame("open") res0: String = open sesame
@author Bill Venners
-
object
RecoverMethods
extends RecoverMethods
Companion object that facilitates the importing of
RecoverMethods
's method as an alternative to mixing it in.Companion object that facilitates the importing of
RecoverMethods
's method as an alternative to mixing it in. One use case is to importRecoverMethods
's method so you can use it in the Scala interpreter. -
object
Retries
extends Retries
Companion object to trait
Retries
that enables its members to be imported as an alternative to mixing them in.Companion object to trait
Retries
that enables its members to be imported as an alternative to mixing them in. -
object
Sequential
extends Serializable
Companion object to class
Sequential
that offers anapply
factory method for creating aSequential
instance.Companion object to class
Sequential
that offers anapply
factory method for creating aSequential
instance.One use case for this object is to run multiple specification-style suites in the Scala interpreter, like this:
scala> Sequential(new MyFirstSuite, new MyNextSuite).execute()
-
object
Stepwise
extends Serializable
Companion object to class
Stepwise
that offers anapply
factory method for creating aStepwise
instance.Companion object to class
Stepwise
that offers anapply
factory method for creating aStepwise
instance.One use case for this object is to run multiple specification-style suites in the Scala interpreter, like this:
scala> Stepwise(new MyFirstSuite, new MyNextSuite).execute()
-
object
Stopper
Companion object to Stopper that holds a factory method that produces a new
Stopper
whosestopRequested
method returns false until after itsrequestStop
has been invoked.Companion object to Stopper that holds a factory method that produces a new
Stopper
whosestopRequested
method returns false until after itsrequestStop
has been invoked. -
object
StreamlinedXml
extends StreamlinedXml
Companion object that facilitates the importing of
StreamlinedXml
members as an alternative to mixing it the trait.Companion object that facilitates the importing of
StreamlinedXml
members as an alternative to mixing it the trait. One use case is to importStreamlinedXml
members so you can use them in the Scala interpreter. -
object
StreamlinedXmlEquality
extends StreamlinedXmlEquality
Companion object that facilitates the importing of
StreamlinedXmlEquality
members as an alternative to mixing it the trait.Companion object that facilitates the importing of
StreamlinedXmlEquality
members as an alternative to mixing it the trait. One use case is to importStreamlinedXmlEquality
members so you can use them in the Scala interpreter. -
object
StreamlinedXmlNormMethods
extends StreamlinedXmlNormMethods
Companion object that facilitates the importing of
StreamlinedXmlNormMethods
members as an alternative to mixing it the trait.Companion object that facilitates the importing of
StreamlinedXmlNormMethods
members as an alternative to mixing it the trait. One use case is to importStreamlinedXmlNormMethods
's implicit so you can use it in the Scala interpreter. -
object
Succeeded
extends Outcome with scalatest.compatible.Assertion with Product with Serializable
Outcome for a test that succeeded.
Outcome for a test that succeeded.
Note: the difference between this
Succeeded
object and the similarly namedSucceededStatus
object is that this object indicates one test (or assertion) succeeded, whereas theSucceededStatus
object indicates the absence of any failed tests or aborted suites during a run. Both are used as the result type ofSuite
lifecycle methods, butSucceeded
is a possible result ofwithFixture
, whereasSucceededStatus
is a possible result ofrun
,runNestedSuites
,runTests
, orrunTest
. In short,Succeeded
is always just about one test (or assertion), whereasSucceededStatus
could be about something larger: multiple tests or an entire suite. -
object
SucceededStatus
extends Status with Serializable
Singleton status that represents an already completed run with no tests failed and no suites aborted.
Singleton status that represents an already completed run with no tests failed and no suites aborted.
Note: the difference between this
SucceededStatus
object and the similarly namedSucceeded
object is that theSucceeded
object indicates one test succeeded, whereas thisSucceededStatus
object indicates the absence of any failed tests or aborted suites during a run. Both are used as the result type ofSuite
lifecycle methods, butSucceeded
is a possible result ofwithFixture
, whereasSucceededStatus
is a possible result ofrun
,runNestedSuites
,runTests
, orrunTest
. In short,Succeeded
is always just about one test, whereasSucceededStatus
could be about something larger: multiple tests or an entire suite. -
object
Suites
extends Serializable
Companion object to class
Suites
that offers anapply
factory method for creating aSuites
instance.Companion object to class
Suites
that offers anapply
factory method for creating aSuites
instance.One use case for this object is to run multiple specification-style suites in the Scala interpreter, like this:
scala> Suites(new MyFirstSuite, new MyNextSuite).execute()
-
object
Tag
Companion object for
Tag
, which offers a factory method.Companion object for
Tag
, which offers a factory method. - object Tracker
-
object
TryValues
extends TryValues
Companion object that facilitates the importing of
TryValues
members as an alternative to mixing it in.Companion object that facilitates the importing of
TryValues
members as an alternative to mixing it in. One use case is to importTryValues
's members so you can usesuccess
andfailure
onTry
in the Scala interpreter. -
object
run
Singleton object providing an
apply
method for the ScalaTest shell and amain
method for ScalaTest's simple runner.Singleton object providing an
apply
method for the ScalaTest shell and amain
method for ScalaTest's simple runner.The
apply
method can be used in the ScalaTest Shell (its DSL for the Scala interpreter) in this way:scala> import org.scalatest._ import org.scalatest._ scala> class ArithmeticSuite extends FunSuite with Matchers { | test("addition works") { | 1 + 1 should equal (2) | } | ignore("subtraction works") { | 1 - 1 should equal (0) | } | test("multiplication works") { | 1 * 1 should equal (2) | } | test("division works") (pending) | } defined class ArithmeticSuite scala> run(new ArithmeticSuite) ArithmeticSuite: - addition works - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** 1 did not equal 2 (
:16) - division works (pending)The last command is calling the
apply
method on therun
singleton object. In other words, you could alternatively call it this way:scala> run.apply(new ArithmeticSuite) ArithmeticSuite: - addition works - subtraction works !!! IGNORED !!! - multiplication works *** FAILED *** 1 did not equal 2 (
:16) - division works (pending)The
run
singleton object also serves a different purpose. Itsmain
method allows users to "run"run
as a Scala application. ScalaTest'sRunner
application is very powerful, but doesn't provide the simplest out-of-box experience for people trying ScalaTest for the first time. For example, to run anExampleSpec
in the unnamed package from the directory where it is compiled withRunner
's standard out reporter requires this command:$ scala -cp scalatest-RELEASE.jar org.scalatest.tools.Runner -R . -o -s ExampleSpec
Running it with the
run
application is simpler:$ scala -cp scalatest-RELEASE.jar org.scalatest.run ExampleSpec