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

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

The newest version!
/*
 * 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 exceptions.TestCanceledException
import scala.reflect.Manifest
import Assertions.areEqualComparingArraysStructurally
import org.scalautils.TripleEquals
import exceptions.StackDepthExceptionHelper.getStackDepthFun
import exceptions.StackDepthException.toExceptionFunction
import Assertions.NormalResult
import org.scalautils.Prettifier

/**
 * 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. *

* *

* 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". *

* *

* You can provide an alternate 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")
 * 
* * *

Intercepted exceptions

* *

* Sometimes you need to test whether a method throws an expected exception under certain circumstances, such * as when invalid arguments are passed to the method. You can do this in the JUnit 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 an intercept * method. You use it like this: *

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

* This code behaves much like the previous example. If charAt throws an instance of IndexOutOfBoundsException, * intercept will return that exception. But if charAt completes normally, or throws a different * exception, intercept will complete abruptly with a TestFailedException. intercept 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. *

* * *

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'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. *

* * *

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 can optionally provide a clue string, or use === to get a more detailed * 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 { 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 simple quality checks of this form: *

* *
    *
  • assert(a == b)
  • *
  • assert(a != b)
  • *
  • assert(a === b)
  • *
  • assert(a !== b)
  • *
* *

* Any other form of expression will just get a plain-old TestFailedException at this time. 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, not Option[String] to be * the default in tests. This makes === consistent between tests and production code. If you have pre-existing * code you wrote under ScalaTest 1.x, in which you are expecting=== to return an Option[String], * use can get that behavior back by mixing in trait LegacyTripleEquals. *

* * @param condition the boolean condition to assert * @throws TestFailedException if the condition is false. */ def assert(condition: Boolean): Unit = macro AssertionsMacro.assert /** * Helper class used by code generated by the assert macro. */ class AssertionsHelper { private def append(currentMessage: Option[String], clueOpt: Option[Any]) = clueOpt match { case Some(clue) => currentMessage match { case Some(msg) => // clue.toString.head is guaranteed to work, because append() only called if clue.toString != "" val firstChar = clue.toString.head if (firstChar.isWhitespace || firstChar == '.' || firstChar == ',' || firstChar == ';') Some(msg + clue.toString) else Some(msg + " " + clue.toString) case None => Some(clue.toString) } case None => currentMessage } def getObjectsForFailureMessage(a: Any, b: Any) = a match { case aEqualizer: org.scalautils.TripleEqualsSupport#Equalizer[_] => Suite.getObjectsForFailureMessage(aEqualizer.leftSide, b) case aEqualizer: org.scalautils.TripleEqualsSupport#CheckingEqualizer[_] => Suite.getObjectsForFailureMessage(aEqualizer.leftSide, b) case _ => Suite.getObjectsForFailureMessage(a, b) } /** * Assert that the passed in expression is true, else fail with TestFailedException. * * @param expression Boolean expression to assert for * @param clue optional clue to be included in TestFailedException's error message when assertion failed */ def macroAssert(expression: Boolean, clue: Option[Any]) { if (clue == null) throw new NullPointerException("clue was null") if (!expression) throw newAssertionFailedException(if (clue.isDefined) Some(clue.get + "") else None, None, "Assertions.scala", "macroAssert", 2) } /** * Assert that the passed in expression is true, else fail with TestFailedException. * * @param left the LHS of the expression * @param operator the operator of the expression * @param right the RHS of the expression * @param expression Boolean expression to assert for * @param clue optional clue to be included in TestFailedException's error message when assertion failed */ def macroAssert(left: Any, operator: String, right: Any, expression: Boolean, clue: Option[Any]) { if (clue == null) throw new NullPointerException("clue was null") if (!expression) { throw operator match { case "==" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newAssertionFailedException(append(Some(FailureMessages("didNotEqual", leftee, rightee)), clue), None, "Assertions.scala", "macroAssert", 2) case "===" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newAssertionFailedException(append(Some(FailureMessages("didNotEqual", leftee, rightee)), clue), None, "Assertions.scala", "macroAssert", 2) case "!=" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newAssertionFailedException(append(Some(FailureMessages("equaled", leftee, rightee)), clue), None, "Assertions.scala", "macroAssert", 2) case "!==" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newAssertionFailedException(append(Some(FailureMessages("equaled", leftee, rightee)), clue), None, "Assertions.scala", "macroAssert", 2) /*case ">" => newAssertionFailedException(append(Some(FailureMessages("wasNotGreaterThan", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case ">=" => newAssertionFailedException(append(Some(FailureMessages("wasNotGreaterThanOrEqualTo", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case "<" => newAssertionFailedException(append(Some(FailureMessages("wasNotLessThan", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case "<=" => newAssertionFailedException(append(Some(FailureMessages("wasNotLessThanOrEqualTo", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2)*/ case _ => throw newAssertionFailedException(if (clue.isDefined) Some(clue.get + "") else None, None, "Assertions.scala", "macroAssert", 2) } } } /** * Assume that the passed in expression is true, else throw TestCanceledException. * * @param expression Boolean expression to assume for * @param clue optional clue to be included in TestCanceledException's error message when assertion failed */ def macroAssume(expression: Boolean, clue: Option[Any]) { if (clue == null) throw new NullPointerException("clue was null") if (!expression) throw newTestCanceledException(if (clue.isDefined) Some(clue.get + "") else None, None, "Assertions.scala", "macroAssume", 2) } /** * Assume that the passed in expression is true, else throw TestCanceledException. * * @param left the LHS of the expression * @param operator the operator of the expression * @param right the RHS of the expression * @param expression Boolean expression to assume for * @param clue optional clue to be included in TestCanceledException's error message when assertion failed */ def macroAssume(left: Any, operator: String, right: Any, expression: Boolean, clue: Option[Any]) { if (clue == null) throw new NullPointerException("clue was null") if (!expression) { throw operator match { case "==" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newTestCanceledException(append(Some(FailureMessages("didNotEqual", leftee, rightee)), clue), None, "Assertions.scala", "macroAssume", 2) case "===" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newTestCanceledException(append(Some(FailureMessages("didNotEqual", leftee, rightee)), clue), None, "Assertions.scala", "macroAssume", 2) case "!=" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newTestCanceledException(append(Some(FailureMessages("equaled", leftee, rightee)), clue), None, "Assertions.scala", "macroAssume", 2) case "!==" => val (leftee, rightee) = getObjectsForFailureMessage(left, right) newTestCanceledException(append(Some(FailureMessages("equaled", leftee, rightee)), clue), None, "Assertions.scala", "macroAssume", 2) /*case ">" => newTestCanceledException(append(Some(FailureMessages("wasNotGreaterThan", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case ">=" => newTestCanceledException(append(Some(FailureMessages("wasNotGreaterThanOrEqualTo", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case "<" => newTestCanceledException(append(Some(FailureMessages("wasNotLessThan", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2) case "<=" => newTestCanceledException(append(Some(FailureMessages("wasNotLessThanOrEqualTo", left, right)), clue), None, "Assertions.scala", "macroAssertTrue", 2)*/ case _ => throw newTestCanceledException(if (clue.isDefined) Some(clue.get + "") else None, None, "Assertions.scala", "macroAssume", 2) } } } } /** * Helper instance used by code generated by macro assertion. */ val assertionsHelper = new AssertionsHelper private[scalatest] def newAssertionFailedException(optionalMessage: Option[Any], optionalCause: Option[Throwable], stackDepth: Int): Throwable = (optionalMessage, optionalCause) match { case (None, None) => new TestFailedException(stackDepth) case (None, Some(cause)) => new TestFailedException(cause, stackDepth) case (Some(message), None) => new TestFailedException(message.toString, stackDepth) case (Some(message), Some(cause)) => new TestFailedException(message.toString, cause, stackDepth) } private[scalatest] def newAssertionFailedException(optionalMessage: Option[String], optionalCause: Option[Throwable], fileName: String, methodName: String, stackDepthAdjustment: Int): Throwable = new exceptions.TestFailedException(toExceptionFunction(optionalMessage), optionalCause, getStackDepthFun(fileName, methodName, stackDepthAdjustment)) private def newTestCanceledException(optionalMessage: Option[Any], optionalCause: Option[Throwable], stackDepth: Int): Throwable = (optionalMessage, optionalCause) match { case (None, None) => new TestCanceledException(stackDepth) case (None, Some(cause)) => new TestCanceledException(cause, stackDepth) case (Some(message), None) => new TestCanceledException(message.toString, stackDepth) case (Some(message), Some(cause)) => new TestCanceledException(message.toString, cause, stackDepth) } private[scalatest] def newTestCanceledException(optionalMessage: Option[String], optionalCause: Option[Throwable], fileName: String, methodName: String, stackDepthAdjustment: Int): Throwable = new TestCanceledException(toExceptionFunction(optionalMessage), optionalCause, getStackDepthFun(fileName, methodName, stackDepthAdjustment), 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 the * String obtained by invoking toString on the * specified clue as the exception's detail message. * * @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 NullPointerException if message is null. */ def assert(condition: Boolean, clue: Any): Unit = macro AssertionsMacro.assertWithClue /** * Assert that an Option[String] is None. * If the condition is None, this method returns normally. * Else, it throws TestFailedException with the String * value of the Some, as well as the * String obtained by invoking toString on the * specified clue, * included in the TestFailedException's detail message. * *

* This form of assert is usually called in conjunction with an * implicit conversion to Equalizer, using a === comparison, as in: *

* *
   * assert(a === b, "extra info reported if assertion fails")
   * 
* *

* For more information on how this mechanism works, see the [[org.scalautils.TripleEqualsSupport.Equalizer documentation for * Equalizer]]. *

* * @param o the Option[String] to assert * @param clue An object whose toString method returns a message to include in a failure report. * @throws TestFailedException if the Option[String] is Some. * @throws NullPointerException if message is null. */ @deprecated("This method has been deprecated in favor of macro assertion and will be removed in a future version of ScalaTest. If you need this, please copy the source code into your own trait instead.") def assert(o: Option[String], clue: Any) { o match { case Some(s) => throw newAssertionFailedException(Some(clue + "\n" + s), None, 4) case None => } } /** * Assert that an Option[String] is None. * If the condition is None, this method returns normally. * Else, it throws TestFailedException with the String * value of the Some included in the TestFailedException's * detail message. * *

* This form of assert is usually called in conjunction with an * implicit conversion to Equalizer, using a === comparison, as in: *

* *
   * assert(a === b)
   * 
* *

* For more information on how this mechanism works, see the [[org.scalautils.TripleEqualsSupport.Equalizer documentation for * Equalizer]]. *

* * @param o the Option[String] to assert * @throws TestFailedException if the Option[String] is Some. */ @deprecated("This method has been deprecated in favor of macro assertion and will be removed in a future version of ScalaTest. If you need this, please copy the source code into your own trait instead.") def assert(o: Option[String]) { o match { case Some(s) => throw newAssertionFailedException(Some(s), None, 4) case None => } } /** * 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 simple quality checks of this form: *

* *
    *
  • assume(a == b)
  • *
  • assume(a != b)
  • *
  • assume(a === b)
  • *
  • assume(a !== b)
  • *
* *

* Any other form of expression will just get a plain-old TestCanceledException at this time. 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, not Option[String] to be * the default in tests. This makes === consistent between tests and production code. If you have pre-existing * code you wrote under ScalaTest 1.x, in which you are expecting=== to return an Option[String], * use can get that behavior back by mixing in trait LegacyTripleEquals. *

* * @param condition the boolean condition to assume * @throws TestCanceledException if the condition is false. */ def assume(condition: Boolean): Unit = 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 the * String obtained by invoking toString on the * specified clue as the exception's detail message. * * @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 NullPointerException if message is null. */ def assume(condition: Boolean, clue: Any): Unit = macro AssertionsMacro.assumeWithClue /** * Assume that an Option[String] is None. * If the condition is None, this method returns normally. * Else, it throws TestCanceledException with the String * value of the Some, as well as the * String obtained by invoking toString on the * specified clue, * included in the TestCanceledException's detail message. * *

* This form of assume is usually called in conjunction with an * implicit conversion to Equalizer, using a === comparison, as in: *

* *
   * assume(a === b, "extra info reported if assertion fails")
   * 
* *

* For more information on how this mechanism works, see the [[org.scalautils.TripleEqualsSupport.Equalizer documentation for * Equalizer]]. *

* * @param o the Option[String] to assert * @param clue An object whose toString method returns a message to include in a failure report. * @throws TestCanceledException if the Option[String] is Some. * @throws NullPointerException if message is null. */ @deprecated("This method has been deprecated in favor of macro assumption and will be removed in a future version of ScalaTest. If you need this, please copy the source code into your own trait instead.") def assume(o: Option[String], clue: Any) { o match { case Some(s) => throw newTestCanceledException(Some(clue + "\n" + s), None, 3) case None => } } /** * Assume that an Option[String] is None. * If the condition is None, this method returns normally. * Else, it throws TestCanceledException with the String * value of the Some included in the TestCanceledException's * detail message. * *

* This form of assume is usually called in conjunction with an * implicit conversion to Equalizer, using a === comparison, as in: *

* *
   * assume(a === b)
   * 
* *

* For more information on how this mechanism works, see the [[org.scalautils.TripleEqualsSupport.Equalizer documentation for * Equalizer]]. *

* * @param o the Option[String] to assert * @throws TestCanceledException if the Option[String] is Some. */ // def assume(o: Option[String]) = throwIfSome(o, (a: Any) => newTestCanceledException(Some(a.toString), None, 3)) @deprecated("This method has been deprecated in favor of macro assumption and will be removed in a future version of ScalaTest. If you need this, please copy the source code into your own trait instead.") def assume(o: Option[String]) { o match { case Some(s) => throw newTestCanceledException(Some(s), None, 3) case None => } } /** * Asserts that a given string snippet of code does not pass the Scala 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: *

* *
   * 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. *

* * @param code the snippet of code that should not type check */ def assertTypeError(code: String): Unit = macro CompileMacro.assertTypeErrorImpl /* * * Implicit conversion from Any to Equalizer, used to enable * assertions with === comparisons. * *

* For more information * on this mechanism, see the documentation for Equalizer. *

* *

* Because trait Suite mixes in Assertions, this implicit conversion will always be * available by default in ScalaTest Suites. This is the only implicit conversion that is in scope by default in every * ScalaTest Suite. Other implicit conversions offered by ScalaTest, such as those that support the matchers DSL * or invokePrivate, must be explicitly invited into your test code, either by mixing in a trait or importing the * members of its companion object. The reason ScalaTest requires you to invite in implicit conversions (with the exception of the * implicit conversion for === operator) is because if one of ScalaTest's implicit conversions clashes with an * implicit conversion used in the code you are trying to test, your program won't compile. Thus there is a chance that if you * are ever trying to use a library or test some code that also offers an implicit conversion involving a === operator, * you could run into the problem of a compiler error due to an ambiguous implicit conversion. If that happens, you can turn off * the implicit conversion offered by this convertToEqualizer method simply by overriding the method in your * Suite subclass, but not marking it as implicit: *

* *
   * // In your Suite subclass
   * override def convertToEqualizer(left: Any) = new Equalizer(left)
   * 
* * @param left the object whose type to convert to Equalizer. * @throws NullPointerException if left is null. */ // implicit def convertToEqualizer(left: Any) = new Equalizer(left) /* * Intercept and return an instance of the passed exception class (or an instance of a subclass of the * passed class), which is expected to be thrown by the passed function value. This method invokes the passed * function. If it throws an exception that's an instance of the passed class or one of its * subclasses, this method returns that exception. Else, whether the passed function returns normally * or completes abruptly with a different exception, this method throws TestFailedException * whose detail message includes the String obtained by invoking toString on the passed clue. * *

* Note that the passed Class may represent any type, 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 pass in a class instance for * a trait. 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. *

* * @param message An object whose toString method returns a message to include in a failure report. * @param f the function value that should throw the expected exception * @return the intercepted exception, if it is of the expected type * @throws TestFailedException if the passed function does not result in a value equal to the * passed expected value. def intercept[T <: AnyRef](message: Any)(f: => Any)(implicit manifest: Manifest[T]): T = { val clazz = manifest.erasure.asInstanceOf[Class[T]] val messagePrefix = if (message.toString.trim.isEmpty) "" else (message +"\n") 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(messagePrefix + s), Some(u), 4) } else { Some(u) } } } caught match { case None => val message = messagePrefix + Resources("exceptionExpected", clazz.getName) throw newAssertionFailedException(Some(message), None, 4) case Some(e) => e.asInstanceOf[T] // I know this cast will succeed, becuase iSAssignableFrom succeeded above } } THIS DOESN'T OVERLOAD. I THINK I'LL EITHER NEED TO USE interceptWithMessage OR JUST LEAVE IT OUT. FOR NOW I'LL LEAVE IT OUT. */ /** * 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. *

* * @param f the function value that should throw the expected exception * @param manifest an implicit Manifest 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 * passed expected value. */ def intercept[T <: AnyRef](f: => Any)(implicit manifest: Manifest[T]): T = { val clazz = manifest.erasure.asInstanceOf[Class[T]] 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), 4) } else { Some(u) } } } caught match { case None => val message = Resources("exceptionExpected", clazz.getName) throw newAssertionFailedException(Some(message), None, 4) case Some(e) => e.asInstanceOf[T] // I know this cast will succeed, becuase isAssignableFrom succeeded above } } /* * Intercept and return an instance of the passed exception class (or an instance of a subclass of the * passed class), which is expected to be thrown by the passed function value. This method invokes the passed * function. If it throws an exception that's an instance of the passed class or one of its * subclasses, 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 passed Class may represent any type, 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 pass in a class instance for * a trait. 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. *

* * @param clazz a type to which the expected exception class is assignable, i.e., the exception should be an instance of the type represented by clazz. * @param f the function value that should throw the expected exception * @return the intercepted exception, if * @throws TestFailedException if the passed function does not complete abruptly with an exception that is assignable to the * passed Class. * @throws IllegalArgumentException if the passed clazz is not Throwable or * one of its subclasses. */ /* def intercept[T <: AnyRef](clazz: java.lang.Class[T])(f: => Unit): T = { // intercept(clazz)(f)(manifest) "hi".asInstanceOf[T] } */ /* def intercept[T <: AnyRef](clazz: java.lang.Class[T])(f: => Unit)(implicit manifest: Manifest[T]): T = { intercept(clazz)(f)(manifest) } */ /** * Trap and return any thrown exception that would normally cause a ScalaTest test to fail, or create and return a new RuntimeException * indicating no exception is thrown. * *

* This method is intended to be used in the Scala interpreter to eliminate large stack traces when trying out ScalaTest assertions and * matcher expressions. It is not intended to be used in regular test code. If you want to ensure that a bit of code throws an expected * exception, use intercept, not trap. Here's an example interpreter session without trap: *

* *
   * scala> import org.scalatest._
   * import org.scalatest._
   *
   * scala> import Matchers._
   * import Matchers._
   *
   * scala> val x = 12
   * a: Int = 12
   *
   * scala> x shouldEqual 13
   * org.scalatest.exceptions.TestFailedException: 12 did not equal 13
   *    at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:449)
   *    at org.scalatest.Assertions$.newAssertionFailedException(Assertions.scala:1203)
   *    at org.scalatest.Assertions$AssertionsHelper.macroAssertTrue(Assertions.scala:417)
   *    at .<init>(<console>:15)
   *    at .<clinit>(<console>)
   *    at .<init>(<console>:7)
   *    at .<clinit>(<console>)
   *    at $print(<console>)
   *    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   *    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   *    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   *    at java.lang.reflect.Method.invoke(Method.java:597)
   *    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:731)
   *    at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:980)
   *    at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:570)
   *    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:601)
   *    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:565)
   *    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:745)
   *    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:790)
   *    at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:702)
   *    at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:566)
   *    at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:573)
   *    at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:576)
   *    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:867)
   *    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
   *    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
   *    at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
   *    at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:822)
   *    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
   *    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
   *    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
   *    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
   * 
* *

* That's a pretty tall stack trace. Here's what it looks like when you use trap: *

* *
   * scala> trap { x shouldEqual 13 }
   * res1: Throwable = org.scalatest.exceptions.TestFailedException: 12 did not equal 13
   * 
* *

* Much less clutter. Bear in mind, however, that if no exception is thrown by the * passed block of code, the trap method will create a new NormalResult * (a subclass of Throwable made for this purpose only) and return that. If the result was the Unit value, it * will simply say that no exception was thrown: *

* *
   * scala> trap { x shouldEqual 12 }
   * res2: Throwable = No exception was thrown.
   * 
* *

* If the passed block of code results in a value other than Unit, the NormalResult's toString will print the value: *

* *
   * scala> trap { "Dude!" }
   * res3: Throwable = No exception was thrown. Instead, result was: "Dude!"
   * 
* *

* Although you can access the result value from the NormalResult, its type is Any and therefore not * very convenient to use. It is not intended that trap be used in test code. The sole intended use case for trap is decluttering * Scala interpreter sessions by eliminating stack traces when executing assertion and matcher expressions. *

*/ def trap[T](f: => T): Throwable = { try { new NormalResult(f) } catch { case ex: Throwable if !Suite.anExceptionThatShouldCauseAnAbort(ex) => ex } } /** * 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) { if (!areEqualComparingArraysStructurally(actual, expected)) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) val fullMsg = AppendedClues.appendClue(s, clue.toString) throw newAssertionFailedException(Some(fullMsg), None, 4) } } /** * This expectResult method has been deprecated; Please use assertResult instead. * *

* To get rid of the deprecation warning, simply replace expectResult with * assertResult. The name expectResult will be used for a different purposes in * a future version of ScalaTest. *

*/ @deprecated("This expectResult method has been deprecated. Please replace all invocations of expectResult with an identical invocation of assertResult instead.") def expectResult(expected: Any, clue: Any)(actual: Any) { if (actual != expected) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) throw newAssertionFailedException(Some(clue + "\n" + s), None, 4) } } /** * This expect method has been deprecated; Please use assertResult instead. * *

* To get rid of the deprecation warning, simply replace expect with * assertResult. The name expect will be used for a different purposes in * a future version of ScalaTest. *

*/ @deprecated("This expect method has been deprecated. Please replace all invocations of expect with an identical invocation of assertResult instead.") def expect(expected: Any, clue: Any)(actual: Any) { if (actual != expected) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) throw newAssertionFailedException(Some(clue + "\n" + s), None, 4) } } /** * 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) { if (!areEqualComparingArraysStructurally(actual, expected)) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) throw newAssertionFailedException(Some(s), None, 4) } } /** * This expectResult method has been deprecated; Please use assertResult instead. * *

* To get rid of the deprecation warning, simply replace expectResult with * assertResult. The name expectResult will be used for a different purposes in * a future version of ScalaTest. *

*/ @deprecated("This expectResult method has been deprecated. Please replace all invocations of expectResult with an identical invocation of assertResult instead.") def expectResult(expected: Any)(actual: Any) { if (actual != expected) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) throw newAssertionFailedException(Some(s), None, 4) } } /** * This expect method has been deprecated; Please use assertResult instead. * *

* To get rid of the deprecation warning, simply replace expect with * assertResult. The name expect will be used for a different purposes in * a future version of ScalaTest. *

*/ @deprecated("This expect method has been deprecated. Please replace all invocations of expect with an identical invocation of assertResult instead.") def expect(expected: Any)(actual: Any) { if (actual != expected) { val (act, exp) = Suite.getObjectsForFailureMessage(actual, expected) val s = FailureMessages("expectedButGot", exp, act) throw newAssertionFailedException(Some(s), None, 4) } } /* * 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(): Nothing = { throw newAssertionFailedException(None, None, 4) } /** * 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 NullPointerException if message is null */ def fail(message: String): Nothing = { if (message == null) throw new NullPointerException("message is null") throw newAssertionFailedException(Some(message), None, 4) } /** * 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 NullPointerException if message or cause is null */ def fail(message: String, cause: Throwable): Nothing = { if (message == null) throw new NullPointerException("message is null") if (cause == null) throw new NullPointerException("cause is null") throw newAssertionFailedException(Some(message), Some(cause), 4) } /** * 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 NullPointerException if cause is null */ def fail(cause: Throwable): Nothing = { if (cause == null) throw new NullPointerException("cause is null") throw newAssertionFailedException(None, Some(cause), 4) } /** * Throws TestCanceledException to indicate a test was canceled. */ def cancel(): Nothing = { throw newTestCanceledException(None, None, 3) } /** * 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 NullPointerException if message is null */ def cancel(message: String): Nothing = { if (message == null) throw new NullPointerException("message is null") throw newTestCanceledException(Some(message), None, 3) } /** * 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 NullPointerException if message or cause is null */ def cancel(message: String, cause: Throwable): Nothing = { if (message == null) throw new NullPointerException("message is null") if (cause == null) throw new NullPointerException("cause is null") throw newTestCanceledException(Some(message), Some(cause), 3) } /** * 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 NullPointerException if cause is null */ def cancel(cause: Throwable): Nothing = { if (cause == null) throw new NullPointerException("cause is null") throw newTestCanceledException(None, Some(cause), 3) } /** * 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 NullPointerException if the passed clue is null */ def withClue[T](clue: Any)(fun: => T): T = { if (clue == null) throw new NullPointerException("clue was null") def 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 } */ } /** * 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 { case class NormalResult(result: Any) extends Throwable { override def toString = if (result == ()) Resources("noExceptionWasThrown") else Resources("resultWas", Prettifier.default(result)) } 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 scalautils.DefaultEquality. Put that one in // a singleton and use it in both places. left match { case leftArray: Array[_] => right match { case rightArray: Array[_] => leftArray.deep == rightArray.deep case _ => leftArray.deep == right } case _ => { right match { case rightArray: Array[_] => left == rightArray.deep case _ => left == right } } } } private[scalatest] def checkExpectedException[T](f: => Any, clazz: Class[T], wrongExceptionResourceName: String, exceptionExpectedResourceName: String, stackDepth: Int): T = { val caught = try { f None } catch { case u: Throwable => { if (!clazz.isAssignableFrom(u.getClass)) { val s = Resources(wrongExceptionResourceName, clazz.getName, u.getClass.getName) throw newAssertionFailedException(Some(s), Some(u), stackDepth) } else { Some(u) } } } caught match { case None => val message = Resources(exceptionExpectedResourceName, clazz.getName) throw newAssertionFailedException(Some(message), None, stackDepth) case Some(e) => e.asInstanceOf[T] // I know this cast will succeed, becuase iSAssignableFrom succeeded above } } private[scalatest] def checkNoException(fun: => Any) { val caught = try { fun } catch { case u: Throwable => { val message = Resources("exceptionNotExpected", u.getClass.getName) throw newAssertionFailedException(Some(message), Some(u), 4) } } } private[scalatest] def checkNotException[T <: AnyRef](f: => Any, exceptionNotExpectedResourceName: String)(implicit manifest: Manifest[T]) { val clazz = manifest.erasure.asInstanceOf[Class[T]] try { f } catch { case u: Throwable => { if (clazz.isAssignableFrom(u.getClass)) { val s = Resources(exceptionNotExpectedResourceName, u.getClass.getName) throw newAssertionFailedException(Some(s), Some(u), 4) } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy