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

org.scalatest.matchers.MatcherProducers.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.matchers

/**
 * Provides an implicit conversion on functions that produce Matchers, i.e.,
 * T => Matcher[T] that enables you to modify error messages when composing Matchers.
 *
 * 

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

* *
    *
  1. Transform beGreaterThan from an Int => Matcher[Int] * to a String => Matcher[Int] with the first compose
  2. *
  3. Apply the given (right-hand-side) String to that to get a Matcher[Int] (the first part * of andThen's behavior)
  4. *
  5. 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).
  6. *
*/ 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 MatchResults. * @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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy