org.scalatest.Assertions.scala Maven / Gradle / Ivy
/*
* Copyright 2001-2013 Artima, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.scalatest
import org.scalactic.{Resources => _, FailureMessages => _, _}
import Requirements._
import scala.reflect.ClassTag
import Assertions.areEqualComparingArraysStructurally
import org.scalatest.exceptions.StackDepthException
import org.scalatest.exceptions.StackDepthException.toExceptionFunction
import org.scalatest.exceptions.TestFailedException
import org.scalatest.exceptions.TestPendingException
import org.scalatest.exceptions.TestCanceledException
import ArrayHelper.deep
import org.scalactic.anyvals.NonEmptyArray
/**
* Trait that contains ScalaTest's basic assertion methods.
*
*
* You can use the assertions provided by this trait in any ScalaTest Suite
,
* because Suite
* 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 the Assertions
companion object.
*
*
*
* In any Scala program, you can write assertions by invoking assert
and passing in a Boolean
expression,
* such as:
*
*
*
* val left = 2
* val right = 1
* assert(left == right)
*
*
*
* If the passed expression is true
, assert
will return normally. If false
,
* Scala's assert
will complete abruptly with an AssertionError
. This behavior is provided by
* the assert
method defined in object Predef
, whose members are implicitly imported into every
* Scala source file. This Assertions
trait defines another assert
method that hides the
* one in Predef
. It behaves the same, except that if false
is passed it throws
* TestFailedException
instead of AssertionError
.
* Why? Because unlike AssertionError
, 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's assert
provides better error messages than Scala's assert
.
*
*
*
* If you pass the previous Boolean
expression, left == right
to assert
in a ScalaTest test,
* a failure will be reported that, because assert
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 this assert
* will be: "2 did not equal 1".
*
*
*
* ScalaTest's assert
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. Here are some examples, where a
is 1, b
is 2, c
is 3, d
* is 4, xs
is List(a, b, c)
, and num
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
* to assert
, 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. This Assertions
trait also mixes in the
* TripleEquals
, which gives you a ===
operator
* that allows you to customize Equality
, perform equality checks with numeric
* Tolerance
, and enforce type constraints at compile time with
* sibling traits TypeCheckedTripleEquals
and
* ConversionCheckedTripleEquals
.
*
*
*
* Expected results
*
* Although the assert
macro provides a natural, readable extension to Scala's assert
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 called left
and right
,
* because if one were named expected
and the other actual
, it would be difficult for people to
* remember which was which. To help with these limitations of assertions, Suite
includes a method called assertResult
that
* can be used as an alternative to assert
. To use assertResult
, you place
* the expected value in parentheses after assertResult
, 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 is a - b
. This assertion will fail, and
* the detail message in the TestFailedException
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]
or
* Assertion
. ScalaTest's assertions (including matcher expressions) have result type
* Assertion
, so ending with an assertion will satisfy the compiler.
* If a test body or function body passed to Future.map
does
* not end with type Assertion
, however, you can fix the type error by placing
* succeed
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
throws IndexOutOfBoundsException
as expected, control will transfer
* to the catch case, which does nothing. If, however, charAt
fails to throw an exception,
* the next statement, fail()
, will be run. The fail
method always completes abruptly with
* a TestFailedException
, thereby signaling a failed test.
*
*
*
* To make this common use case easier to express and read, ScalaTest provides two methods:
* assertThrows
and intercept
.
* Here's how you use assertThrows
:
*
*
*
*
* val s = "hi"
* assertThrows[IndexOutOfBoundsException] { // Result type: Assertion
* s.charAt(-1)
* }
*
*
*
* This code behaves much like the previous example. If charAt
throws an instance of IndexOutOfBoundsException
,
* assertThrows
will return Succeeded
. But if charAt
completes normally, or throws a different
* exception, assertThrows
will complete abruptly with a TestFailedException
.
*
*
*
* The intercept
method behaves the same as assertThrows
, except that instead of returning Succeeded
,
* 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 thrown TestFailedException
.
*
*
*
* 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 the assume
method to indicate this at the beginning of
* the test, like this:
*
*
*
* assume(database.isAvailable)
*
*
*
* For each overloaded assert
method, trait Assertions
provides an
* overloaded assume
method with an identical signature and behavior, except the
* assume
methods throw TestCanceledException
whereas the
* assert
methods throw TestFailedException
. As with assert
,
* assume
hides a Scala method in Predef
that performs a similar
* function, but throws AssertionError
. And just as you can with assert
,
* you will get an error message extracted by a macro from the AST passed to assume
, 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 corresponding cancel
method
* with an identical signature and behavior, except the cancel
methods throw
* TestCanceledException
whereas the fail
methods throw
* TestFailedException
. 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
and assertResult
provide a way for a clue to be
* included directly, intercept
does not.
* Here's an example of clues provided directly in assert
:
*
*
*
* 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 failed intercept
call requires using withClue
:
*
*
*
* 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 the ModifiableMessage
trait.
* See the documentation for ModifiableMessage
for more information.
* If you wish to place a clue string after a block of code, see the documentation for
* AppendedClues
.
*
*
*
* Note: ScalaTest's assertTypeError
construct is in part inspired by the illTyped
macro
* of shapeless.
*
*
* @author Bill Venners
*/
trait Assertions extends TripleEquals {
//implicit val prettifier = Prettifier.default
import language.experimental.macros
/**
* Assert that a boolean condition is true.
* If the condition is true
, this method returns normally.
* Else, it throws TestFailedException
.
*
*
* This method is implemented in terms of a Scala macro that will generate a more helpful error message
* for expressions of this form:
*
*
*
* - assert(a == b)
* - assert(a != b)
* - assert(a === b)
* - assert(a !== b)
* - assert(a > b)
* - assert(a >= b)
* - assert(a < b)
* - assert(a <= b)
* - assert(a startsWith "prefix")
* - assert(a endsWith "postfix")
* - assert(a contains "something")
* - assert(a eq b)
* - assert(a ne b)
* - assert(a > 0 && b > 5)
* - assert(a > 0 || b > 5)
* - assert(a.isEmpty)
* - assert(!a.isEmpty)
* - assert(a.isInstanceOf[String])
* - assert(a.length == 8)
* - assert(a.size == 8)
* - assert(a.exists(_ == 8))
*
*
*
* At this time, any other form of expression will get a TestFailedException
with message saying the given
* expression was false. In the future, we will enhance this macro to give helpful error messages in more situations.
* In ScalaTest 2.0, however, this behavior was sufficient to allow the ===
that returns Boolean
* to be the default in tests. This makes ===
consistent between tests and production
* code.
*
*
* @param condition the boolean condition to assert
* @throws TestFailedException if the condition is false
.
*/
def assert(condition: Boolean)(implicit prettifier: Prettifier, pos: source.Position): Assertion = macro AssertionsMacro.assert
private[scalatest] def newAssertionFailedException(optionalMessage: Option[String], optionalCause: Option[Throwable], pos: source.Position, analysis: scala.collection.immutable.IndexedSeq[String]): Throwable =
new org.scalatest.exceptions.TestFailedException(toExceptionFunction(optionalMessage), optionalCause, Left(pos), None, analysis)
private[scalatest] def newTestCanceledException(optionalMessage: Option[String], optionalCause: Option[Throwable], pos: source.Position): Throwable =
new TestCanceledException(toExceptionFunction(optionalMessage), optionalCause, pos, None)
/**
* Assert that a boolean condition, described in String
* message
, is true.
* If the condition is true
, this method returns normally.
* Else, it throws TestFailedException
with a helpful error message
* appended with the String
obtained by invoking toString
on the
* specified clue
as the exception's detail message.
*
*
* This method is implemented in terms of a Scala macro that will generate a more helpful error message
* for expressions of this form:
*
*
*
* - assert(a == b, "a good clue")
* - assert(a != b, "a good clue")
* - assert(a === b, "a good clue")
* - assert(a !== b, "a good clue")
* - assert(a > b, "a good clue")
* - assert(a >= b, "a good clue")
* - assert(a < b, "a good clue")
* - assert(a <= b, "a good clue")
* - assert(a startsWith "prefix", "a good clue")
* - assert(a endsWith "postfix", "a good clue")
* - assert(a contains "something", "a good clue")
* - assert(a eq b, "a good clue")
* - assert(a ne b, "a good clue")
* - assert(a > 0 && b > 5, "a good clue")
* - assert(a > 0 || b > 5, "a good clue")
* - assert(a.isEmpty, "a good clue")
* - assert(!a.isEmpty, "a good clue")
* - assert(a.isInstanceOf[String], "a good clue")
* - assert(a.length == 8, "a good clue")
* - assert(a.size == 8, "a good clue")
* - assert(a.exists(_ == 8), "a good clue")
*
*
*
* At this time, any other form of expression will just get a TestFailedException
with message saying the given
* expression was false. In the future, we will enhance this macro to give helpful error messages in more situations.
* In ScalaTest 2.0, however, this behavior was sufficient to allow the ===
that returns Boolean
* to be the default in tests. This makes ===
consistent between tests and production
* code.
*
*
* @param condition the boolean condition to assert
* @param clue An objects whose toString
method returns a message to include in a failure report.
* @throws TestFailedException if the condition is false
.
* @throws NullArgumentException if message
is null
.
*/
def assert(condition: Boolean, clue: Any)(implicit prettifier: Prettifier, pos: source.Position): Assertion = macro AssertionsMacro.assertWithClue
/**
* Assume that a boolean condition is true.
* If the condition is true
, this method returns normally.
* Else, it throws TestCanceledException
.
*
*
* This method is implemented in terms of a Scala macro that will generate a more helpful error message
* for expressions of this form:
*
*
*
* - assume(a == b)
* - assume(a != b)
* - assume(a === b)
* - assume(a !== b)
* - assume(a > b)
* - assume(a >= b)
* - assume(a < b)
* - assume(a <= b)
* - assume(a startsWith "prefix")
* - assume(a endsWith "postfix")
* - assume(a contains "something")
* - assume(a eq b)
* - assume(a ne b)
* - assume(a > 0 && b > 5)
* - assume(a > 0 || b > 5)
* - assume(a.isEmpty)
* - assume(!a.isEmpty)
* - assume(a.isInstanceOf[String])
* - assume(a.length == 8)
* - assume(a.size == 8)
* - assume(a.exists(_ == 8))
*
*
*
* At this time, any other form of expression will just get a TestCanceledException
with message saying the given
* expression was false. In the future, we will enhance this macro to give helpful error messages in more situations.
* In ScalaTest 2.0, however, this behavior was sufficient to allow the ===
that returns Boolean
* to be the default in tests. This makes ===
consistent between tests and production
* code.
*
*
* @param condition the boolean condition to assume
* @throws TestCanceledException if the condition is false
.
*/
def assume(condition: Boolean)(implicit prettifier: Prettifier, pos: source.Position): Assertion = macro AssertionsMacro.assume
/**
* Assume that a boolean condition, described in String
* message
, is true.
* If the condition is true
, this method returns normally.
* Else, it throws TestCanceledException
with a helpful error message
* appended with String
obtained by invoking toString
on the
* specified clue
as the exception's detail message.
*
*
* This method is implemented in terms of a Scala macro that will generate a more helpful error message
* for expressions of this form:
*
*
*
* - assume(a == b, "a good clue")
* - assume(a != b, "a good clue")
* - assume(a === b, "a good clue")
* - assume(a !== b, "a good clue")
* - assume(a > b, "a good clue")
* - assume(a >= b, "a good clue")
* - assume(a < b, "a good clue")
* - assume(a <= b, "a good clue")
* - assume(a startsWith "prefix", "a good clue")
* - assume(a endsWith "postfix", "a good clue")
* - assume(a contains "something", "a good clue")
* - assume(a eq b, "a good clue")
* - assume(a ne b, "a good clue")
* - assume(a > 0 && b > 5, "a good clue")
* - assume(a > 0 || b > 5, "a good clue")
* - assume(a.isEmpty, "a good clue")
* - assume(!a.isEmpty, "a good clue")
* - assume(a.isInstanceOf[String], "a good clue")
* - assume(a.length == 8, "a good clue")
* - assume(a.size == 8, "a good clue")
* - assume(a.exists(_ == 8), "a good clue")
*
*
*
* At this time, any other form of expression will just get a TestCanceledException
with message saying the given
* expression was false. In the future, we will enhance this macro to give helpful error messages in more situations.
* In ScalaTest 2.0, however, this behavior was sufficient to allow the ===
that returns Boolean
* to be the default in tests. This makes ===
consistent between tests and production
* code.
*
*
* @param condition the boolean condition to assume
* @param clue An objects whose toString
method returns a message to include in a failure report.
* @throws TestCanceledException if the condition is false
.
* @throws NullArgumentException if message
is null
.
*/
def assume(condition: Boolean, clue: Any)(implicit prettifier: Prettifier, pos: source.Position): Assertion = macro AssertionsMacro.assumeWithClue
/**
* Asserts that a given string snippet of code does not pass the Scala type checker, failing if the given
* snippet does not pass the Scala parser.
*
*
* 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:
*
*
*
* assertTypeError("val a: String = 1")
*
*
*
* Although assertTypeError
is implemented with a macro that determines at compile time whether
* the snippet of code represented by the passed string type checks, errors (i.e.,
* snippets of code that do type check) are reported as test failures at runtime.
*
*
*
* Note that the difference between assertTypeError
and assertDoesNotCompile
is
* that assertDoesNotCompile
will succeed if the given code does not compile for any reason,
* whereas assertTypeError
will only succeed if the given code does not compile because of
* a type error. If the given code does not compile because of a syntax error, for example, assertDoesNotCompile
* will return normally but assertTypeError
will throw a TestFailedException
.
*
*
* @param code the snippet of code that should not type check
*/
def assertTypeError(code: String)(implicit pos: source.Position): Assertion = macro CompileMacro.assertTypeErrorImpl
/**
* Asserts that a given string snippet of code does not pass either the Scala parser or type checker.
*
*
* 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 = \"a string")
*
*
*
* Although assertDoesNotCompile
is implemented with a macro that determines at compile time whether
* the snippet of code represented by the passed string doesn't compile, errors (i.e.,
* snippets of code that do compile) are reported as test failures at runtime.
*
*
*
* Note that the difference between assertTypeError
and assertDoesNotCompile
is
* that assertDoesNotCompile
will succeed if the given code does not compile for any reason,
* whereas assertTypeError
will only succeed if the given code does not compile because of
* a type error. If the given code does not compile because of a syntax error, for example, assertDoesNotCompile
* will return normally but assertTypeError
will throw a TestFailedException
.
*
*
* @param code the snippet of code that should not type check
*/
def assertDoesNotCompile(code: String)(implicit pos: source.Position): Assertion = macro CompileMacro.assertDoesNotCompileImpl
/**
* Asserts that a given string snippet of code passes both the Scala parser and type checker.
*
*
* You can use this to make sure a snippet of code compiles:
*
*
*
* assertCompiles("val a: Int = 1")
*
*
*
* Although assertCompiles
is implemented with a macro that determines at compile time whether
* the snippet of code represented by the passed string compiles, errors (i.e.,
* snippets of code that do not compile) are reported as test failures at runtime.
*
*
* @param code the snippet of code that should compile
*/
def assertCompiles(code: String)(implicit pos: source.Position): Assertion = macro CompileMacro.assertCompilesImpl
/**
* Intercept and return an exception that's expected to
* be thrown by the passed function value. The thrown exception must be an instance of the
* type specified by the type parameter of this method. This method invokes the passed
* function. If the function throws an exception that's an instance of the specified type,
* this method returns that exception. Else, whether the passed function returns normally
* or completes abruptly with a different exception, this method throws TestFailedException
.
*
*
* Note that the type specified as this method's type parameter may represent any subtype of
* AnyRef
, not just Throwable
or one of its subclasses. In
* Scala, exceptions can be caught based on traits they implement, so it may at times make sense
* to specify a trait that the intercepted exception's class must mix in. If a class instance is
* passed for a type that could not possibly be used to catch an exception (such as String
,
* for example), this method will complete abruptly with a TestFailedException
.
*
*
*
* Also note that the difference between this method and assertThrows
is that this method
* returns the expected exception, so it lets you perform further assertions on
* that exception. By contrast, the assertThrows
method returns Succeeded
, which means it can
* serve as the last statement in an async- or safe-style suite. assertThrows
also indicates to the reader
* of the code that nothing further is expected about the thrown exception other than its type.
* The recommended usage is to use assertThrows
by default, intercept
only when you
* need to inspect the caught exception further.
*
*
* @param f the function value that should throw the expected exception
* @param classTag an implicit ClassTag
representing the type of the specified
* type parameter.
* @return the intercepted exception, if it is of the expected type
* @throws TestFailedException if the passed function does not complete abruptly with an exception
* that's an instance of the specified type.
*/
def intercept[T <: AnyRef](f: => Any)(implicit classTag: ClassTag[T], pos: source.Position): T = {
val clazz = classTag.runtimeClass
val caught = try {
f
None
}
catch {
case u: Throwable => {
if (!clazz.isAssignableFrom(u.getClass)) {
val s = Resources.wrongException(clazz.getName, u.getClass.getName)
throw newAssertionFailedException(Some(s), Some(u), pos, Vector.empty)
}
else {
Some(u)
}
}
}
caught match {
case None =>
val message = Resources.exceptionExpected(clazz.getName)
throw newAssertionFailedException(Some(message), None, pos, Vector.empty)
case Some(e) => e.asInstanceOf[T] // I know this cast will succeed, becuase isAssignableFrom succeeded above
}
}
/**
* Ensure that an expected exception is thrown by the passed function value. The thrown exception must be an instance of the
* type specified by the type parameter of this method. This method invokes the passed
* function. If the function throws an exception that's an instance of the specified type,
* this method returns Succeeded
. Else, whether the passed function returns normally
* or completes abruptly with a different exception, this method throws TestFailedException
.
*
*
* Note that the type specified as this method's type parameter may represent any subtype of
* AnyRef
, not just Throwable
or one of its subclasses. In
* Scala, exceptions can be caught based on traits they implement, so it may at times make sense
* to specify a trait that the intercepted exception's class must mix in. If a class instance is
* passed for a type that could not possibly be used to catch an exception (such as String
,
* for example), this method will complete abruptly with a TestFailedException
.
*
*
*
* Also note that the difference between this method and intercept
is that this method
* does not return the expected exception, so it does not let you perform further assertions on
* that exception. Instead, this method returns Succeeded
, which means it can
* serve as the last statement in an async- or safe-style suite. It also indicates to the reader
* of the code that nothing further is expected about the thrown exception other than its type.
* The recommended usage is to use assertThrows
by default, intercept
only when you
* need to inspect the caught exception further.
*
*
* @param f the function value that should throw the expected exception
* @param classTag an implicit ClassTag
representing the type of the specified
* type parameter.
* @return the Succeeded
singleton, if an exception of the expected type is thrown
* @throws TestFailedException if the passed function does not complete abruptly with an exception
* that's an instance of the specified type.
*/
def assertThrows[T <: AnyRef](f: => Any)(implicit classTag: ClassTag[T], pos: source.Position): Assertion = {
val clazz = classTag.runtimeClass
val threwExpectedException =
try {
f
false
}
catch {
case u: Throwable => {
if (!clazz.isAssignableFrom(u.getClass)) {
val s = Resources.wrongException(clazz.getName, u.getClass.getName)
throw newAssertionFailedException(Some(s), Some(u), pos, Vector.empty)
}
else true
}
}
if (threwExpectedException) {
Succeeded
}
else {
val message = Resources.exceptionExpected(clazz.getName)
throw newAssertionFailedException(Some(message), None, pos, Vector.empty)
}
}
/**
* Assert that the value passed as expected
equals the value passed as actual
.
* If the actual
equals the expected
* (as determined by ==
), assertResult
returns
* normally. Else, if actual
is not equal to expected
, assertResult
throws a
* TestFailedException
whose detail message includes the expected and actual values, as well as the String
* obtained by invoking toString
on the passed clue
.
*
* @param expected the expected value
* @param clue An object whose toString
method returns a message to include in a failure report.
* @param actual the actual value, which should equal the passed expected
value
* @throws TestFailedException if the passed actual
value does not equal the passed expected
value.
*/
def assertResult(expected: Any, clue: Any)(actual: Any)(implicit prettifier: Prettifier, pos: source.Position): Assertion = {
if (!areEqualComparingArraysStructurally(actual, expected)) {
val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected)
val s = FailureMessages.expectedButGot(prettifier, exp, act)
val fullMsg = AppendedClues.appendClue(s, clue.toString)
throw newAssertionFailedException(Some(fullMsg), None, pos, Vector.empty)
}
Succeeded
}
/**
* Assert that the value passed as expected
equals the value passed as actual
.
* If the actual
value equals the expected
value
* (as determined by ==
), assertResult
returns
* normally. Else, assertResult
throws a
* TestFailedException
whose detail message includes the expected and actual values.
*
* @param expected the expected value
* @param actual the actual value, which should equal the passed expected
value
* @throws TestFailedException if the passed actual
value does not equal the passed expected
value.
*/
def assertResult(expected: Any)(actual: Any)(implicit prettifier: Prettifier, pos: source.Position): Assertion = {
if (!areEqualComparingArraysStructurally(actual, expected)) {
val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected)
val s = FailureMessages.expectedButGot(prettifier, exp, act)
throw newAssertionFailedException(Some(s), None, pos, Vector.empty)
}
Succeeded
}
/*
* TODO: Delete this if sticking with Nothing instead of Unit as result type of fail.
*
* The result type of this and the other overloaded fail
methods is
* Unit
instead of Nothing
, because Nothing
* is a subtype of all other types. If the result type of fail
were
* Nothing
, a block of code that ends in a call to fail()
may
* fail to compile if the block being passed as a by-name parameter or function to an
* overloaded method. The reason is that the compiler selects which overloaded
* method to call based on the static types of the parameters passed. Since
* Nothing
is an instance of everything, it can often make the overloaded
* method selection ambiguous.
*
*
*
* For a concrete example, the Conductor
class
* in package org.scalatest.concurrent
has two overloaded variants of the
* thread
method:
*
*
*
* def thread[T](fun: => T): Thread
*
* def thread[T](name: String)(fun: => T): Thread
*
*
*
* Given these two overloaded methods, the following code will compile given the result type
* of fail
is Unit
, but would not compile if the result type were
* Nothing
:
*
*
*
* thread { fail() }
*
*
*
* If the result type of fail
were Nothing
, the type of the by-name parameter
* would be inferred to be Nothing
, which is a subtype of both T
and
* String
. Thus the call is ambiguous, because the type matches the first parameter type
* of both overloaded thread
methods. Unit
, by constrast, is not
* a subtype of String
, so it only matches one overloaded variant and compiles just fine.
*
*/
/**
* Throws TestFailedException
to indicate a test failed.
*/
def fail()(implicit pos: source.Position): Nothing = { throw newAssertionFailedException(None, None, pos, Vector.empty) }
/**
* Throws TestFailedException
, with the passed
* String
message
as the exception's detail
* message, to indicate a test failed.
*
* @param message A message describing the failure.
* @throws NullArgumentException if message
is null
*/
def fail(message: String)(implicit pos: source.Position): Nothing = {
requireNonNull(message)
throw newAssertionFailedException(Some(message), None, pos, Vector.empty)
}
/**
* Throws TestFailedException
, with the passed
* String
message
as the exception's detail
* message and Throwable
cause, to indicate a test failed.
*
* @param message A message describing the failure.
* @param cause A Throwable
that indicates the cause of the failure.
* @throws NullArgumentException if message
or cause
is null
*/
def fail(message: String, cause: Throwable)(implicit pos: source.Position): Nothing = {
requireNonNull(message, cause)
throw newAssertionFailedException(Some(message), Some(cause), pos, Vector.empty)
}
/**
* Throws TestFailedException
, with the passed
* Throwable
cause, to indicate a test failed.
* The getMessage
method of the thrown TestFailedException
* will return cause.toString
.
*
* @param cause a Throwable
that indicates the cause of the failure.
* @throws NullArgumentException if cause
is null
*/
def fail(cause: Throwable)(implicit pos: source.Position): Nothing = {
requireNonNull(cause)
throw newAssertionFailedException(None, Some(cause), pos, Vector.empty)
}
/**
* Throws TestCanceledException
to indicate a test was canceled.
*/
def cancel()(implicit pos: source.Position): Nothing = { throw newTestCanceledException(None, None, pos) }
/**
* Throws TestCanceledException
, with the passed
* String
message
as the exception's detail
* message, to indicate a test was canceled.
*
* @param message A message describing the cancellation.
* @throws NullArgumentException if message
is null
*/
def cancel(message: String)(implicit pos: source.Position): Nothing = {
requireNonNull(message)
throw newTestCanceledException(Some(message), None, pos)
}
/**
* Throws TestCanceledException
, with the passed
* String
message
as the exception's detail
* message and Throwable
cause, to indicate a test failed.
*
* @param message A message describing the failure.
* @param cause A Throwable
that indicates the cause of the failure.
* @throws NullArgumentException if message
or cause
is null
*/
def cancel(message: String, cause: Throwable)(implicit pos: source.Position): Nothing = {
requireNonNull(message, cause)
throw newTestCanceledException(Some(message), Some(cause), pos)
}
/**
* Throws TestCanceledException
, with the passed
* Throwable
cause, to indicate a test failed.
* The getMessage
method of the thrown TestCanceledException
* will return cause.toString
.
*
* @param cause a Throwable
that indicates the cause of the cancellation.
* @throws NullArgumentException if cause
is null
*/
def cancel(cause: Throwable)(implicit pos: source.Position): Nothing = {
requireNonNull(cause)
throw newTestCanceledException(None, Some(cause), pos)
}
/**
* Executes the block of code passed as the second parameter, and, if it
* completes abruptly with a ModifiableMessage
exception,
* prepends the "clue" string passed as the first parameter to the beginning of the detail message
* of that thrown exception, then rethrows it. If clue does not end in a white space
* character, one space will be added
* between it and the existing detail message (unless the detail message is
* not defined).
*
*
* This method allows you to add more information about what went wrong that will be
* reported when a test fails. Here's an example:
*
*
*
* withClue("(Employee's name was: " + employee.name + ")") {
* intercept[IllegalArgumentException] {
* employee.getTask(-1)
* }
* }
*
*
*
* If an invocation of intercept
completed abruptly with an exception, the resulting message would be something like:
*
*
*
* (Employee's name was Bob Jones) Expected IllegalArgumentException to be thrown, but no exception was thrown
*
*
* @throws NullArgumentException if the passed clue
is null
*/
def withClue[T](clue: Any)(fun: => T): T = {
requireNonNull(clue)
val prepend = (currentMessage: Option[String]) => {
currentMessage match {
case Some(msg) =>
if (clue.toString.last.isWhitespace) // TODO: shouldn't I also check if the head of msg isWhite?
Some(clue.toString + msg)
else
Some(clue.toString + " " + msg)
case None => Some(clue.toString)
}
}
try {
val outcome = fun
outcome match {
case Failed(e: org.scalatest.exceptions.ModifiableMessage[_]) if clue.toString != "" =>
Failed(e.modifyMessage(prepend)).asInstanceOf[T]
case Canceled(e: org.scalatest.exceptions.ModifiableMessage[_]) if clue.toString != "" =>
Canceled(e.modifyMessage(prepend)).asInstanceOf[T]
case _ => outcome
}
}
catch {
case e: org.scalatest.exceptions.ModifiableMessage[_] =>
if (clue != "")
throw e.modifyMessage(prepend)
else
throw e
}
}
/* Hold off on this for now. See how people do with the simple one that takes an Any.
def withClueFunction(sfun: Option[String] => Option[String])(fun: => Unit) {
fun
}
*/
/**
* Throws TestPendingException
to indicate a test is pending.
*
*
* A pending test is one that has been given a name but is not yet implemented. The purpose of
* pending tests is to facilitate a style of testing in which documentation of behavior is sketched
* out before tests are written to verify that behavior (and often, the before the behavior of
* the system being tested is itself implemented). Such sketches form a kind of specification of
* what tests and functionality to implement later.
*
*
*
* To support this style of testing, a test can be given a name that specifies one
* bit of behavior required by the system being tested. The test can also include some code that
* sends more information about the behavior to the reporter when the tests run. At the end of the test,
* it can call method pending
, which will cause it to complete abruptly with TestPendingException
.
* Because tests in ScalaTest can be designated as pending with TestPendingException
, both the test name and any information
* sent to the reporter when running the test can appear in the report of a test run. (In other words,
* the code of a pending test is executed just like any other test.) However, because the test completes abruptly
* with TestPendingException
, the test will be reported as pending, to indicate
* the actual test, and possibly the functionality it is intended to test, has not yet been implemented.
*
*
*
* Note: This method always completes abruptly with a TestPendingException
. Thus it always has a side
* effect. Methods with side effects are usually invoked with parentheses, as in pending()
. This
* method is defined as a parameterless method, in flagrant contradiction to recommended Scala style, because it
* forms a kind of DSL for pending tests. It enables tests in suites such as FunSuite
or FunSpec
* to be denoted by placing "(pending)
" after the test name, as in:
*
*
*
* test("that style rules are not laws") (pending)
*
*
*
* Readers of the code see "pending" in parentheses, which looks like a little note attached to the test name to indicate
* it is pending. Whereas "(pending())
looks more like a method call, "(pending)
" lets readers
* stay at a higher level, forgetting how it is implemented and just focusing on the intent of the programmer who wrote the code.
*
*/
def pending: Assertion with PendingStatement = { throw new TestPendingException }
/**
* Execute the passed block of code, and if it completes abruptly, throw TestPendingException
, else
* throw TestFailedException
.
*
*
* This method can be used to temporarily change a failing test into a pending test in such a way that it will
* automatically turn back into a failing test once the problem originally causing the test to fail has been fixed.
* At that point, you need only remove the pendingUntilFixed
call. In other words, a
* pendingUntilFixed
surrounding a block of code that isn't broken is treated as a test failure.
* The motivation for this behavior is to encourage people to remove pendingUntilFixed
calls when
* there are no longer needed.
*
*
*
* This method facilitates a style of testing in which tests are written before the code they test. Sometimes you may
* encounter a test failure that requires more functionality than you want to tackle without writing more tests. In this
* case you can mark the bit of test code causing the failure with pendingUntilFixed
. You can then write more
* tests and functionality that eventually will get your production code to a point where the original test won't fail anymore.
* At this point the code block marked with pendingUntilFixed
will no longer throw an exception (because the
* problem has been fixed). This will in turn cause pendingUntilFixed
to throw TestFailedException
* with a detail message explaining you need to go back and remove the pendingUntilFixed
call as the problem orginally
* causing your test code to fail has been fixed.
*
*
* @param f a block of code, which if it completes abruptly, should trigger a TestPendingException
* @throws TestPendingException if the passed block of code completes abruptly with an Exception
or AssertionError
*/
def pendingUntilFixed(f: => Unit)(implicit pos: source.Position): Assertion with PendingStatement = {
val isPending =
try {
f
false
}
catch {
case _: Exception => true
case _: AssertionError => true
}
if (isPending)
throw new TestPendingException
else
throw new TestFailedException((sde: StackDepthException) => Some(Resources.pendingUntilFixed), None, pos)
}
/**
* The Succeeded
singleton.
*
*
* You can use succeed
to solve a type error when an async test
* does not end in either Future[Assertion]
or Assertion
.
* Because Assertion
is a type alias for Succeeded.type
,
* putting succeed
at the end of a test body (or at the end of a
* function being used to map the final future of a test body) will solve
* the type error.
*
*/
final val succeed: Assertion = Succeeded
}
/**
* Companion object that facilitates the importing of Assertions
members as
* an alternative to mixing it in. One use case is to import Assertions
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
*
*
* @author Bill Venners
*/
object Assertions extends Assertions {
private[scalatest] def areEqualComparingArraysStructurally(left: Any, right: Any): Boolean = {
// Prior to 2.0 this only called .deep if both sides were arrays. Loosened it
// when nearing 2.0.M6 to call .deep if either left or right side is an array.
// TODO: this is the same algo as in scalactic.DefaultEquality. Put that one in
// a singleton and use it in both places.
left match {
case leftArray: Array[_] =>
right match {
case rightArray: Array[_] => deep(leftArray) == deep(rightArray)
case rightNonEmptyArray: NonEmptyArray[_] => deep(leftArray) == deep(rightNonEmptyArray.toArray)
case _ => deep(leftArray) == right
}
case leftNonEmptyArray: NonEmptyArray[_] =>
right match {
case rightArray: Array[_] => deep(leftNonEmptyArray.toArray) == deep(rightArray)
case rightNonEmptyArray: NonEmptyArray[_] => deep(leftNonEmptyArray.toArray) == deep(rightNonEmptyArray.toArray)
case _ => deep(leftNonEmptyArray.toArray) == right
}
case other => {
right match {
case rightArray: Array[_] => left == deep(rightArray)
case rightNonEmptyArray: NonEmptyArray[_] => left == deep(rightNonEmptyArray.toArray)
case _ => left == right
}
}
}
}
/**
* Helper class used by code generated by the assert
macro.
*/
class AssertionsHelper {
private def append(currentMessage: Option[String], clue: Any) = {
val clueStr = clue.toString
if (clueStr.isEmpty)
currentMessage
else {
currentMessage match {
case Some(msg) =>
// clue.toString.head is guaranteed to work, because the previous if check that clue.toString != ""
val firstChar = clueStr.head
if (firstChar.isWhitespace || firstChar == '.' || firstChar == ',' || firstChar == ';')
Some(msg + clueStr)
else
Some(msg + " " + clueStr)
case None => Some(clueStr)
}
}
}
/**
* Assert that the passed in Bool
is true
, else fail with TestFailedException
.
*
* @param bool the Bool
to assert for
* @param clue optional clue to be included in TestFailedException
's error message when assertion failed
*/
def macroAssert(bool: Bool, clue: Any, prettifier: Prettifier, pos: source.Position): Assertion = {
requireNonNull(clue)(prettifier, pos)
if (!bool.value) {
val failureMessage = if (Bool.isSimpleWithoutExpressionText(bool)) None else Some(bool.failureMessage)
throw newAssertionFailedException(append(failureMessage, clue), None, pos, bool.analysis)
}
Succeeded
}
/**
* Assume that the passed in Bool
is true
, else throw TestCanceledException
.
*
* @param bool the Bool
to assume for
* @param clue optional clue to be included in TestCanceledException
's error message when assertion failed
*/
def macroAssume(bool: Bool, clue: Any, prettifier: Prettifier, pos: source.Position): Assertion = {
requireNonNull(clue)(prettifier, pos)
if (!bool.value) {
val failureMessage = if (Bool.isSimpleWithoutExpressionText(bool)) None else Some(bool.failureMessage)
throw newTestCanceledException(append(failureMessage, clue), None, pos)
}
Succeeded
}
}
/**
* Helper instance used by code generated by macro assertion.
*/
val assertionsHelper = new AssertionsHelper
}