ScalaTest/Scalactic 2.2.0 includes the enhancements and deprecations listed below. No source code using ScalaTest/ScalaUtils 2.1.x should break, but you will likely need to do a clean build to upgrade.
For information on how to include ScalaTest in your project, see the download page. For information on how to use Scalactic in your production code, see its download page.
Assertions
error messagesDiagrammedAssertions
Requirements
Snapshots
TestRegistration
traitIn 2.2.0, org.scalautils
has been renamed to org.scalactic
(rhymes with "galactic")'>). All existing code using org.scalautils
directly will continue to work, but will receive a deprecation warning. Please change scalautils
to scalactic
when convenient, because after a lengthy deprecation period, org.scalautils
will be removed.
ScalaUtils started out as a small library carved out of ScalaTest that made sense for production code as well as tests. It has since been growing more mature as a production-code library focused on quality, and deserved a more distinctive name. The name "scalactic" comes from this 2009 scala-internals discussion. I proposed it as a word to mean Scala-like, like "Pythonic" for Python. It never caught on for that, so instead I thought it might work well as the new name for ScalaUtils. Also, because Scalactic precedes ScalaTest alphabetically, its documentation shows up at the top of the combined Scaladoc instead of the bottom, where ScalaUtils has less prominently appeared since 2.0.
Somewhat ironically, that 2009 scala-internals thread was a debate about Paul Phillip's addition of a times
method to RichInt
, which would make the imperative looping syntax, 5 times println
, supported by the Scala standard library by default. Martin Odersky indicated he felt the syntax was not Scala-like, and asked, "what's the analogue of 'Pythonic' in Scala?" By suggesting scalactic I was implying that times
on Int
was not "scalactic," yet since its initial release of ScalaUtils, that very syntax has been available via its TimesOnInt
trait. Since ScalaUtils is now called Scalactic, TimesOnInt
is now part of Scalactic. Perhaps what's "scalactic" about this is that the 5 times println
syntax is available from a third-party library rather than by default from the standard library.
Note that although org.scalautils
package name in your source code will continue to work during the deprecation period, there will be no artifacts with an org.scalautils
group ID released for 2.2.0. If you were using ScalaUtils only through ScalaTest, then your build will continue to work as before. But if you were using ScalaUtils standalone in your production code, you may see an error like:
[warn] :::::::::::::::::::::::::::::::::::::::::::::: [warn] :: UNRESOLVED DEPENDENCIES :: [warn] :::::::::::::::::::::::::::::::::::::::::::::: [warn] :: org.scalautils#scalautils_2.10;2.2.0: not found [warn] ::::::::::::::::::::::::::::::::::::::::::::::
If so, you'll need to change "scalautils"
to "scalactic"
in your build. For example, in an sbt build, you
would need to change:
libraryDependencies += "org.scalautils" % "scalautils" % "2.1.7" % "test"
to:
libraryDependencies += "org.scalactic" % "scalactic" % "2.2.0" % "test"
Assertions
error messagesFor ScalaTest 2.2.0, we have enhanced the macro that produces error messages from the assert
methods of trait Assertions
.
Here are some examples:
scala> import org.scalatest._ import org.scalatest._ scala> import Assertions._ import Assertions._ scala> val a = 1 a: Int = 1 scala> val b = 2 b: Int = 2 scala> val c = 3 c: Int = 3 scala> val d = 4 d: Int = 4 scala> val num = 1.0 num: Double = 1.0 scala> assert(a == b || c >= d) org.scalatest.exceptions.TestFailedException: 1 did not equal 2, and 3 was not greater than or equal to 4 at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> val xs = List(1, 2, 3) xs: List[Int] = List(1, 2, 3) scala> assert(xs.exists(_ == 4)) org.scalatest.exceptions.TestFailedException: List(1, 2, 3) did not contain 4 at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... 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) ... scala> assert(num.isInstanceOf[Int]) org.scalatest.exceptions.TestFailedException: 1.0 was not instance of scala.Int at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(Some(2).isEmpty) org.scalatest.exceptions.TestFailedException: Some(2) was not empty at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
The macro works by recognizing patterns in the AST of the expression passed to assert
and,
for a finite set of common expressions, giving an error message that an equivalent ScalaTest matcher
expression would give. 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:
scala> assert(None.isDefined) org.scalatest.exceptions.TestFailedException: scala.None.isDefined was false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ... scala> assert(xs.exists(i => i > 10)) org.scalatest.exceptions.TestFailedException: xs.exists(((i: Int) => i.>(10))) was false at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
In the future we hope to improve the default error messages by showing values at the leaf nodes, and to the extent possible, showing a representation of the original AST before it was desugared. Getting back to the original AST before desugaring is difficult (if not impossible) using macros as they are currently defined, but hopefully this use case will help motivate some improvements in that direction to Scala macros.
Note that if a clue string is supplied in the assertion, it will be appended to the macro-generated error message:
scala> val p = true p: Boolean = true scala> val q = false q: Boolean = false scala> assert(p == q, s", though now that I think of it, $p has never equaled $q!") org.scalatest.exceptions.TestFailedException: true did not equal false, though now that I think of it, true has never equaled false! at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:422) ...
Lastly, the assume
methods received the same enhancement as the assert
methods. Compared to assert
, you'll get
the same error message given the same expression from assume
, but via a thrown TestCanceledException
instead of a TestFailedException
.
Here's an example:
scala> assume(a + 1 == b && c != d - 1) org.scalatest.exceptions.TestCanceledException: 2 equaled 2, but 3 equaled 3 at org.scalatest.Assertions$class.newTestCanceledException(Assertions.scala:433) ...
DiagrammedAssertions
Building on work that Peter Niederwieser has done in Spock and Expecty, ScalaTest 2.2.0 introduces trait DiagrammedAssertions
. This trait extends >Assertions
and overrides its assert
methods, modifying the default macro to give error messages that show the original line of code and a value for each part of the expression. 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
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 how
DiagrammedAssertions
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) ...
Requirements
Scalactic includes a new trait that was not in ScalaUtils called Requirements
. This trait applies macros to the task of pre-condition checking through methods named require
, requireState
, and requireNonNull
. These three methods aim to improve error messages provided when a pre-condition check fails at runtime in production code. Although it is recommended practice to supply helpful error messages when doing pre-condition checks, often people don't. Instead of this:
scala> val length = 5 length: Int = 5 scala> val idx = 6 idx: Int = 6 scala> require(idx >= 0 && idx <= length, "index, " + idx + ", was less than zero or greater than or equal to length, " + length) java.lang.IllegalArgumentException: requirement failed: index, 6, was less than zero or greater than or equal to length, 5 at scala.Predef$.require(Predef.scala:233) ...
People write simply:
scala> require(idx >= 0 && idx <= length) java.lang.IllegalArgumentException: requirement failed at scala.Predef$.require(Predef.scala:221) ...
Note that the detail message of the IllegalArgumentException
thrown by the previous line of code is simply, "requirement failed"
.
Such messages often end up in a log file or bug report, where a better error message can save you time in debugging the problem.
By importing the members of Requirements
(or mixing in its companion trait), you'll get a more helpful error message extracted by a macro, whether or not
a clue message is provided:
scala> import org.scalactic._ import org.scalactic._ scala> import Requirements._ import Requirements._ scala> require(idx >= 0 && idx <= length) java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5 at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56) ... scala> require(idx >= 0 && idx <= length, "(hopefully that helps)") java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5 (hopefully that helps) at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56) ...
The requireState
method provides identical error messages to require
, but throws IllegalStateException
instead of IllegalArgumentException
:
scala> val connectionOpen = false connectionOpen: Boolean = false scala> requireState(connectionOpen) java.lang.IllegalStateException: connectionOpen was false at org.scalactic.Requirements$RequirementsHelper.macroRequireState(Requirements.scala:71) ...
Thus, whereas the require
methods throw the Java platform's standard exception indicating a passed argument violated a precondition, IllegalArgumentException
, the requireState
methods throw the standard exception indicating an object's method was invoked when the object was in an inappropriate state for that method, IllegalStateException
.
The requireNonNull
method takes one or more variables as arguments and throws NullPointerException
with an error messages that includes the variable names if any are null
. Here's an example:
scala> val e: String = null e: String = null scala> val f: java.util.Date = null f: java.util.Date = null scala> requireNonNull(a, b, c, d, e, f) java.lang.NullPointerException: e and f were null at org.scalactic.Requirements$RequirementsHelper.macroRequireNonNull(Requirements.scala:101) ...
Although trait Requirements
can help you debug problems that occur in production, bear in mind that a much better alternative is to make it impossible for such events to occur at all. Use the type system to ensure that all pre-conditions are met so that the compiler can find broken pre-conditions and point them out with compiler error messages. When this is not possible or practical, however, trait Requirements
is helpful.
Snapshots
Scalactic also includes a new trait named Snapshots
that was not part of ScalaUtils. Snapshots
provides a snap
method that takes one or more arguments and results in a SnapshotSeq
, whose toString
lists the names and values of each argument. Its intended use case is to help you write debug and log messages that give a "snapshot")'> of program state. Here's an example:
scala> import Snapshots._ import Snapshots._ scala> snap(a, b, c, d, e, f) res3: org.scalactic.SnapshotSeq = a was 1, b was 2, c was 3, d was 4, e was null, f was null
SnapshotSeq
offers a lines
method that places each variable name/value pair on its own line:
scala> snap(a, b, c, d, e, f).lines res4: String = a was 1 b was 2 c was 3 d was 4 e was null f was null
Or, because a SnapshotSeq
is a IndexedSeq[Snapshot]
, you can process it just like any other Seq
, for example:
scala> snap(a, b, c, d, e, f).mkString("Wow! ", ", and ", ". That's so awesome!") res6: String = Wow! a was 1, and b was 2, and c was 3, and d was 4, and e was null, and f was null. That's so awesome!
Added a matchPattern
matcher that uses a macro to enforce intended usage. ScalaTest's Inside
trait has long provided an
inside
construct that allows you to perform assertions on values obtained via a pattern match. Here's an example:
scala> import org.scalatest._ import org.scalatest._ scala> import Matchers._ import Matchers._ scala> import Inside._ import Inside._ scala> case class Name(first: String, middle: String, last: String) defined class Name scala> val name = Name("Jane", "Q", "Programmer") name: Name = Name(Jane,Q,Programmer) scala> inside(name) { case Name(first, _, _) => | first should startWith ("S") | } org.scalatest.exceptions.TestFailedException: "Jane" did not start with substring "S", inside Name(Jane,Q,Programmer) at org.scalatest.MatchersHelper$.newTestFailedException(MatchersHelper.scala:160) ...
This inside
construct can also be used simply to ensure a value matches a given pattern, like this:
scala> inside(name) { case Name("Sandra", _, _) => } org.scalatest.exceptions.TestFailedException: The partial function passed as the second parameter to inside was not defined at the value passed as the first parameter to inside, which was: Name(Jane,Q,Programmer) at org.scalatest.Inside$class.inside(Inside.scala:126) ...
However, the programmer intent in the previous code is less obvious--did they just want to ensure the pattern matched or did they forget
to add an assertion? The new matchPattern
syntax is designed to make this more obvious for users of >Matchers
.
Here's what it looks like:
scala> name should matchPattern { case Name("Sandra", _, _) => } org.scalatest.exceptions.TestFailedException: Name(Jane,Q,Programmer) did not match the given pattern at org.scalatest.MatchersHelper$.newTestFailedException(MatchersHelper.scala:160) ...
The reason matchPattern
was not added earlier was that until the advent of macros, there was no way to prevent this kind
of usage:
name should matchPattern { case Name(first, _, _) => first should startWith ("S") }
The above use case is what inside
is for. To make user code consistent, matchPattern
uses a macro to ensure
only a pattern is provided--no code is allowed to the right of the rocket. Here's what you'll see at compile-time if you accidentally attempt this:
scala> name should matchPattern { case Name(first, _, _) => | first should startWith ("S") | } <console>:21: error: No code is allowed to the right of rocket symbols (=>) in a partial function passed to matchPattern, because matchPattern is intended only for ensuring that an expression matches a pattern. If you want to make further assertions after a successful pattern match, use org.scalatest.Inside instead. first should startWith ("S") ^
Added should
compile
and shouldNot
typeCheck
syntax to Matchers
and assertCompiles
and assertDoesNotCompile
methods to Assertions
:
should
compile
and assertCompiles
: fails if the given snippet of code does not compile for any reason, else succeedsshouldNot
compile
and assertDoesNotCompile
: succeeds the given snippet of code does not compile for any reason, else failsshouldNot
typeCheck
and assertTypeError
: succeeds only if the given snippet of code fails to compile because of a type error; fails if the code either compiles or fails to compile because a syntax (not type) errorTestRegistration
traitAdded trait TestRegistration
(and fixture.TestRegistration
), which offers methods registerTest
and registerIgnoredTest
. ScalaTests's style traits that register tests as functions--FunSuite
, FunSpec
, FeatureSpec
, FlatSpec
, FreeSpec
, PropSpec
, and WordSpec
--now extend TestRegistration
. This provides a more generic way to define tests programmatically, such as this use case from the >Discipline project. Currently trait Discipline
will only work with FunSuite
. With this enhancement to ScalaTest Discipline
can be made to work with any style in which test are functions registered at construction time.
Another use case for TestRegistration
is to make a type error to mix traits that facilitate registration of shared tests into a style that does not support test registration. For example, the AllBrowsersPerSuite
and AllBrowsersPerTest
traits of the ScalaTest + Play library only work if mixed into a trait that support registration of tests as functions. When ScalaTest + Play is released against 2.2.0, TestRegistration
will be added as a self-type to AllBrowsersPerSuite
and AllBrowsersPerTest
to make it a compiler error if someone attempts to mix them into a style that does not support test registration.
Page
out of WebBrowser
to become a sibling in package >>org.scalatest.selenium
, to make it easier to create reusabled Page
classes.recover
and recoverWith
methods to Or
, modeled after similar methods in scala.util.Try
.WebBrowser
trait's Selenium DSL to better support browsers that do not fully support the new elements. (Contributed by Matt Hughes.)-o
and -e
parameters passed from sbt to ScalaTest's Framework
implementation, so that you can
have a -oS
, for example, in your testOptions
in the build file and override it, say, with test-only ... -- -oF
.-l
and -n
parameters passed to Runner
. The driving use case for this change is to make it easier
to write sbt code that will include or exclude different tags in different situations.@Inherited
. Annotated all the tag annotations in the
org.scalatest.tags
method with @Inherited
so they could be used in this way. Did not make org.scalatest.Ignore
@Inherited
.
NoArg
thread safe. Originally it was not synchronized because its
intended use is by one thread. Although this behavior was described in its Scaladoc documentation, this was changed in 2.2.0 to make it more bulletproof.
NoArg
thread safe. Originally it was not synchronized because its
intended use is by one thread. Although this behavior was described in its Scaladoc documentation, this was changed in 2.2.0 to make it more bulletproof.
ScalaTestVersion
to the org.scalatest
package object and ScalacticVersion
to the org.scalactic
package object that return a string giving the version number.
no-specialization
flag. See the 2.1.7 release notes for details.
done
on a Runner
instance in ScalaTest's Framework
implementation
waits until the ServerSocket
thread has received an event signalling the run is completed.-o
configuration will be used when running sbt in fork mode.Visit ScalaTest Release Notes for links to the release notes of all previous versions, or step back in time by visiting the release notes for the previous version.
ScalaTest is brought to you by Bill Venners and Artima.
ScalaTest is free, open-source software
released under the Apache
2.0 license.
If your company loves ScalaTest, please consider sponsoring the project.
Copyright © 2009-2025 Artima, Inc. All Rights Reserved.