org.scalatest.matchers.MatcherProducers.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.matchers
/**
* Provides an implicit conversion on functions that produce Matcher
s, i.e.,
* T => Matcher[T]
that enables you to modify error messages when composing Matcher
s.
*
*
* For an example of using MatcherProducers
, see the Composing matchers
* section in the main documentation for trait Matcher
.
*
*
* @author Bill Venners
* @author Chee Seng
*/
trait MatcherProducers {
/**
* Class used via an implicit conversion that adds composeTwice
, mapResult
, and
* mapArgs
methods to functions that produce a Matcher
.
*/
class Composifier[T](f: T => Matcher[T]) {
/**
* Produces a new “matcher producer” function of type U => Matcher[U]
from the
* T => Matcher[T]
(named f
) passed to the Composifier
constructor and the given
* T => U
transformation function, g
.
*
*
* The result of composeTwice
is the result of the following function composition expression:
*
*
*
* (f compose g) andThen (_ compose g)
*
*
*
* You would use composeTwice
if you want to create a new matcher producer from an existing one, by transforming
* both the left and right sides of the expression with the same transformation function. As an example, the
* expression “be > 7
” produces a Matcher[Int]
:
*
*
*
* scala> import org.scalatest._
* import org.scalatest._
*
* scala> import Matchers._
* import Matchers._
*
* scala> val beGreaterThanSeven = be > 7
* beGreaterThanSeven: org.scalatest.matchers.Matcher[Int] = be > 7
*
*
*
* Given this Matcher[Int]
, you can now use it in a should
expression like this:
*
*
*
* scala> 8 should beGreaterThanSeven
*
* scala> 6 should beGreaterThanSeven
* org.scalatest.exceptions.TestFailedException: 6 was not greater than 7
* ...
*
*
*
* You can create a more general “matcher producer” function like this:
*
*
*
* scala> val beGreaterThan = { (i: Int) => be > i }
* beGreaterThan: Int => org.scalatest.matchers.Matcher[Int] = <function1>
*
* scala> 8 should beGreaterThan (7)
*
* scala> 8 should beGreaterThan (9)
* org.scalatest.exceptions.TestFailedException: 8 was not greater than 9
*
*
*
* Given beGreaterThan
matcher producer function, you can create matcher producer function
* that takes a String
and produces a Matcher[String]
given a function from
* Int => String
using composeTwice
:
*
*
*
* scala> val stringToInt = { (s: String) => s.toInt }
* stringToInt: String => Int = <function1>
*
* scala> val beAsIntsGreaterThan = beGreaterThan composeTwice stringToInt
* beAsIntsGreaterThan: String => org.scalatest.matchers.Matcher[String] = <function1>
*
* scala> "7" should beAsIntsGreaterThan ("6")
*
* scala> "7" should beAsIntsGreaterThan ("8")
* org.scalatest.exceptions.TestFailedException: 7 was not greater than 8
* ...
*
*
*
* The composeTwice
method is just a shorthand for this function composition expression:
*
*
*
* scala> val beAsIntsGreaterThan =
* (beGreaterThan compose stringToInt) andThen (_ compose stringToInt)
* beAsIntsGreaterThan: String => org.scalatest.matchers.Matcher[String] = <function1>
*
* scala> "7" should beAsIntsGreaterThan ("6")
*
* scala> "7" should beAsIntsGreaterThan ("8")
* org.scalatest.exceptions.TestFailedException: 7 was not greater than 8
*
*
*
* The first part of that expression, beGreaterThan
compose
stringToInt
,
* gives you a new matcher producer function that given a String
will produce a Matcher[Int]
:
*
*
*
* scala> val beAsIntGreaterThan = beGreaterThan compose stringToInt
* beAsIntGreaterThan: String => org.scalatest.matchers.Matcher[Int] = <function1>
*
*
*
* This compose
method is inherited from Function1
: on any Function1
,
* (f
compose
g)(x)
means f(g(x))
. You can use this
* matcher producer like this:
*
*
*
* scala> 7 should beAsIntGreaterThan ("6")
*
* scala> 7 should beAsIntGreaterThan ("8")
* org.scalatest.exceptions.TestFailedException: 7 was not greater than 8
*
*
*
* To get a matcher producer that will allow you to put a string on the right-hand-side, you'll need to transform
* the String
=>
Matcher[Int]
to a String
=>
* Matcher[String]
. To accomplish this you can first just apply the function to get a Matcher[Int]
,
* like this:
*
*
*
* scala> val beGreaterThanEight = beAsIntGreaterThan ("8")
* beGreaterThanEight: org.scalatest.matchers.Matcher[Int] = be > 8
*
* scala> 9 should beGreaterThanEight
*
* scala> 7 should beGreaterThanEight
* org.scalatest.exceptions.TestFailedException: 7 was not greater than 8
*
*
*
* To transform beGreaterThanEight
, a Matcher[Int]
, to a Matcher[String]
,
* you can again use compose
. A ScalaTest Matcher[T]
is a Scala function type T
* =>
MatchResult
. To get a Matcher[String]
therefore, just call
* compose
on the Matcher[Int]
and pass in a function from String
=>
* Int
:
*
*
*
* scala> val beAsIntGreaterThanEight = beGreaterThanEight compose stringToInt
* beAsIntGreaterThanEight: org.scalatest.matchers.Matcher[String] = <function1>
*
*
*
* After the second call to compose
, therefore, you have what you want:
*
*
*
* scala> "9" should beAsIntGreaterThanEight
*
* scala> "7" should beAsIntGreaterThanEight
* org.scalatest.exceptions.TestFailedException: 7 was not greater than 8
*
*
*
* So in summary, what the result of (beGreaterThan
compose
stringToInt)
andThen
* (_
compose
stringToInt)
will do once it is applied to a (right-hand-side)
* String
, is:
*
*
*
* - Transform
beGreaterThan
from an Int
=>
Matcher[Int]
* to a String
=>
Matcher[Int]
with the first compose
* - Apply the given (right-hand-side)
String
to that to get a Matcher[Int]
(the first part
* of andThen
's behavior)
* - Pass the resulting
Matcher[Int]
to the given function, _
compose
* stringToInt
, which will transform the Matcher[Int]
to a Matcher[String]
(the
* second part of the andThen
behavior).
*
*/
def composeTwice[U](g: U => T): U => Matcher[U] = (f compose g) andThen (_ compose g)
/**
* Returns a function that given a T
will return a Matcher[T]
that will produce the
* MatchResult
produced by f
(passed to the Composifier
constructor)
* transformed by the given prettify
function.
*
* @param prettify a function with which to transform MatchResult
s.
* @return a new Matcher
producer function that produces prettified error messages
*/
def mapResult(prettify: MatchResult => MatchResult): T => Matcher[T] =
(o: T) => f(o) mapResult prettify
/**
* Returns a function that given a T
will return a Matcher[T]
that will produce the
* MatchResult
produced by f
(passed to the Composifier
constructor)
* with arguments transformed by the given prettify
function.
*
* @param prettify a function with which to transform the arguments of error messages.
* @return a new Matcher
producer function that produces prettified error messages
*/
def mapArgs(prettify: Any => String): T => Matcher[T] =
(o: T) => f(o) mapArgs prettify
}
import scala.language.implicitConversions
/**
* Implicit conversion that converts a function of T => Matcher[T]
to an object that has
* composeTwice
, mapResult
and mapArgs
methods.
*
* The following shows how this trait is used to compose twice and modify error messages:
*
*
* import org.scalatest._
* import matchers._
* import MatcherProducers._
*
* val f = be > (_: Int)
* val g = (_: String).toInt
*
* // f composeTwice g means: (f compose g) andThen (_ compose g)
* val beAsIntsGreaterThan =
* f composeTwice g mapResult { mr =>
* mr.copy(
* failureMessageArgs =
* mr.failureMessageArgs.map((LazyArg(_) { "\"" + _.toString + "\".toInt"})),
* negatedFailureMessageArgs =
* mr.negatedFailureMessageArgs.map((LazyArg(_) { "\"" + _.toString + "\".toInt"})),
* midSentenceFailureMessageArgs =
* mr.midSentenceFailureMessageArgs.map((LazyArg(_) { "\"" + _.toString + "\".toInt"})),
* midSentenceNegatedFailureMessageArgs =
* mr.midSentenceNegatedFailureMessageArgs.map((LazyArg(_) { "\"" + _.toString + "\".toInt"}))
* )
* }
*
* "7" should beAsIntsGreaterThan ("8")
*
*
* The last assertion will fail with message like this:
*
*
* "7".toInt was not greater than "8".toInt
*
*
* @param f a function that takes a T
and return a Matcher[T]
* @tparam T the type used by function f
* @return an object that has composeTwice
, mapResult
and mapArgs
methods.
*/
implicit def convertToComposifier[T](f: T => Matcher[T]): Composifier[T] = new Composifier(f)
}
/**
* Companion object that facilitates the importing of MatcherProducers
members as
* an alternative to mixing it in. One use case is to import MatcherProducers
's members so you can use
* MatcherProducers
in the Scala interpreter.
*/
object MatcherProducers extends MatcherProducers