org.scalatest.EitherValues.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 => _, _}
import org.scalatest.exceptions.StackDepthException
import org.scalatest.exceptions.TestFailedException
/**
* Trait that provides an implicit conversion that adds value
(when you expect a Right)
* and left.value
(when you expect a Left) methods
* to Either
, which will return the selected value of the Either
if defined,
* or throw TestFailedException
if not.
*
*
* This construct allows you to express in one statement that an Either
should be left or right
* and that its value should meet some expectation. Here's are some examples:
*
*
*
* either1.value should be > 9
* either2.left.value should be ("Muchos problemas")
*
*
*
* Or, using assertions instead of matcher expressions:
*
*
*
* assert(either1.value > 9)
* assert(either2.left.value === "Muchos problemas")
*
*
*
* Were you to simply invoke left.get
on the Either
,
* if the Either
wasn't defined as expected (e.g., it was a Right
when you expected a Left
), it
* would throw a NoSuchElementException
:
*
*
*
* val either: Either[String, Int] = Right(9)
*
* either.left.get should be > "Muchos problemas" // either.right.get throws NoSuchElementException
*
*
*
* The NoSuchElementException
would cause the test to fail, but without providing a stack depth pointing
* to the failing line of test code. This stack depth, provided by TestFailedException
(and a
* few other ScalaTest exceptions), makes it quicker for
* users to navigate to the cause of the failure. Without EitherValues
, to get
* a stack depth exception you would need to make two statements, like this:
*
*
*
* val either: Either[String, Int] = Right(9)
*
* either should be ('left) // throws TestFailedException
* either.left.get should be > "Muchos problemas"
*
*
*
* The EitherValues
trait allows you to state that more concisely:
*
*
*
* val either: Either[String, Int] = Left("Muchas problemas")
*
* either.left.value should be > 9 // either.left.value throws TestFailedException
*
*/
trait EitherValues extends Serializable {
import scala.language.implicitConversions
/**
* Implicit conversion that adds a value
method to LeftProjection
.
*
* @param leftProj the LeftProjection
on which to add the value
method
*/
implicit def convertLeftProjectionToValuable[L, R](leftProj: Either.LeftProjection[L, R])(implicit pos: source.Position): LeftValuable[L, R] = new LeftValuable(leftProj, pos)
/**
* Implicit conversion that adds a value
method to RightProjection
.
*
* @param rightProj the RightProjection
on which to add the value
method
*/
@deprecated("The .right.value syntax on Either has been deprecated and will be removed in a future version of ScalaTest. Please use .value instead.")
implicit def convertRightProjectionToValuable[L, R](rightProj: Either.RightProjection[L, R])(implicit pos: source.Position): RightValuable[L, R] = new RightValuable(rightProj, pos)
/**
* Implicit conversion that adds a value
method to Either
.
* This method is right biased and is the equivalent of calling either.right.value
.
*
* @param either the Either
on which to add the value
method
*/
implicit def convertEitherToValuable[L, R](either: Either[L, R])(implicit pos: source.Position): EitherValuable[L, R] = new EitherValuable(either, pos)
/**
* Wrapper class that adds a value
method to LeftProjection
, allowing
* you to make statements like:
*
*
* either.left.value should be > 9
*
*
* @param leftProj A LeftProjection
to convert to LeftValuable
, which provides the
* value
method.
*/
class LeftValuable[L, R](leftProj: Either.LeftProjection[L, R], pos: source.Position) extends Serializable {
/**
* Returns the Left
value contained in the wrapped LeftProjection
, if defined as a Left
, else throws TestFailedException
with
* a detail message indicating the Either
was defined as a Right
, not a Left
.
*/
def value: L = {
try {
leftProj.get
}
catch {
case cause: NoSuchElementException =>
val e = leftProj.e
val p = pos
throw new TestFailedException((_: StackDepthException) => Some(Resources.eitherLeftValueNotDefined(e)), Some(cause), p)
}
}
}
/**
* Wrapper class that adds a value
method to RightProjection
, allowing
* you to make statements like:
*
*
* either.right.value should be > 9
*
*
* @param rightProj A RightProjection
to convert to RightValuable
, which provides the
* value
method.
*/
class RightValuable[L, R](rightProj: Either.RightProjection[L, R], pos: source.Position) extends Serializable {
/**
* Returns the Right
value contained in the wrapped RightProjection
, if defined as a Right
, else throws TestFailedException
with
* a detail message indicating the Either
was defined as a Right
, not a Left
.
*/
def value: R = {
try {
rightProj.get
}
catch {
case cause: NoSuchElementException =>
val e = rightProj.e
val p = pos
throw new TestFailedException((_: StackDepthException) => Some(Resources.eitherRightValueNotDefined(e)), Some(cause), p)
}
}
}
/**
* Wrapper class that adds a value
method to Either
, allowing
* you to make statements to inspect the value if a Right, like:
*
*
* either.value should be > 9
*
*
* @param either An Either
to convert to EitherValuable
, which provides the
* value
method.
*/
class EitherValuable[L, R](either: Either[L, R], pos: source.Position) extends Serializable {
/**
* Returns the Right
value contained in the wrapped RightProjection
, if defined as a Right
, else throws TestFailedException
with
* a detail message indicating the Either
was defined as a Right
, not a Left
.
*/
def value: R = {
either match {
case Right(r) => r
case _ =>
val e = either
val p = pos
throw new TestFailedException((_: StackDepthException) => Some(Resources.eitherValueNotDefined(e)), None, p)
}
}
}
}
/**
* Companion object that facilitates the importing of ValueEither
members as
* an alternative to mixing it in. One use case is to import EitherValues
's members so you can use
* left.value
and right.value
on Either
in the Scala interpreter:
*
*
* $ scala -cp scalatest-1.7.jar
* Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
* Type in expressions to have them evaluated.
* Type :help for more information.
*
* scala> import org.scalatest._
* import org.scalatest._
*
* scala> import matchers.Matchers._
* import matchers.Matchers._
*
* scala> import EitherValues._
* import EitherValues._
*
* scala> val e: Either[String, Int] = Left("Muchas problemas")
* e: Either[String,Int] = Left(Muchos problemas)
*
* scala> e.left.value should be ("Muchos problemas")
*
* scala> e.value should be < 9
* org.scalatest.TestFailedException: The Either on which value was invoked was not defined.
* at org.scalatest.EitherValues$RightValuable.value(EitherValues.scala:148)
* at .<init>(<console>:18)
* ...
*
*/
object EitherValues extends EitherValues