org.scalatest.PartialFunctionValues.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._
import org.scalatest.exceptions.StackDepthException
import org.scalatest.exceptions.TestFailedException
/**
* Trait that provides an implicit conversion that adds a valueAt
method
* to PartialFunction
, which will return the value (result) of the function applied to the argument passed to valueAt
,
* or throw TestFailedException
if the partial function is not defined at the argument.
*
*
* This construct allows you to express in one statement that a partial function should be defined for a particular input,
* and that its result value should meet some expectation. Here's an example:
*
*
*
* pf.valueAt("IV") should equal (4)
*
*
*
* Or, using an assertion instead of a matcher expression:
*
*
*
* assert(pf.valueAt("IV") === 4)
*
*
*
* Were you to simply invoke apply
on the PartialFunction
, passing in an input value,
* if the partial function wasn't defined at that input, it would throw some exception, but likely not one
* that provides a stack depth:
*
*
*
* // Note: a Map[K, V] is a PartialFunction[K, V]
* val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4)
*
* pf("V") should equal (5) // pf("V") throws NoSuchElementException
*
*
*
* The NoSuchElementException
thrown in this situation would cause the test to fail, but without providing a stack depth pointing
* to the failing line of test code. This stack depth, provided by TestFailedException
(and a
* few other ScalaTest exceptions), makes it quicker for
* users to navigate to the cause of the failure. Without PartialFunctionValues
, to get
* a stack depth exception you would need to make two statements, like this:
*
*
*
* val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4)
*
* pf.isDefinedAt("V") should be (true) // throws TestFailedException
* pf("V") should equal (5)
*
*
*
* The PartialFunctionValues
trait allows you to state that more concisely:
*
*
*
* val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4)
*
* pf.valueAt("V") should equal (5) // pf.valueAt("V") throws TestFailedException
*
*/
trait PartialFunctionValues {
import scala.language.implicitConversions
/**
* Implicit conversion that adds a valueAt
method to PartialFunction
.
*
* @param pf the PartialFunction
on which to add the valueAt
method
*/
implicit def convertPartialFunctionToValuable[A, B](pf: PartialFunction[A, B])(implicit pos: source.Position): Valuable[A, B] = new Valuable(pf, pos)
/**
* Wrapper class that adds a valueAt
method to PartialFunction
, allowing
* you to make statements like:
*
*
* pf.valueAt("VI") should equal (6)
*
*
* @param pf An PartialFunction
to convert to Valuable
, which provides the valueAt
method.
*/
class Valuable[A, B](pf: PartialFunction[A, B], pos: source.Position) {
/**
* Returns the result of applying the wrapped PartialFunction
to the passed input, if it is defined at that input, else
* throws TestFailedException
with a detail message indicating the PartialFunction
was not defined at the given input.
*/
def valueAt(input: A): B = {
if (pf.isDefinedAt(input)) {
pf.apply(input)
}
else
throw new TestFailedException((_: StackDepthException) => Some(Resources.partialFunctionValueNotDefined(input.toString)), None, pos)
}
}
}
/**
* Companion object that facilitates the importing of PartialFunctionValues
members as
* an alternative to mixing it in. One use case is to import PartialFunctionValues
's members so you can use
* the valueAt
method on PartialFunction
in the Scala interpreter:
*
*
* $ scala -cp scalatest-1.7.jar
* Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
* Type in expressions to have them evaluated.
* Type :help for more information.
*
* scala> import org.scalatest._
* import org.scalatest._
*
* scala> import matchers.Matchers._
* import matchers.Matchers._
*
* scala> import PartialFunctionValues._
* import PartialFunctionValues._
*
* scala> val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4)
* pf: PartialFunction[String,Int] = Map(I -> 1, II -> 2, III -> 3, IV -> 4)
*
* scala> pf("IV") should equal (4)
*
* scala> pf("V") should equal (5)
* java.util.NoSuchElementException: key not found: V
* at scala.collection.MapLike$class.default(MapLike.scala:224)
* at scala.collection.immutable.Map$Map4.default(Map.scala:167)
* ...
*
*/
object PartialFunctionValues extends PartialFunctionValues