org.scalautils.Equality.scala Maven / Gradle / Ivy
Show all versions of scalatest_2.11.0-M5 Show documentation
/*
* 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.scalautils
/**
* Defines a custom way to determine equality for a type.
*
*
* Equality
enables you to define alternate notions of equality for types that can be used
* with ScalaUtil's ===
and !==
syntax and ScalaTest's matcher syntax.
*
*
*
* For example, say you have a case class that includes a Double
value:
*
*
*
* scala> case class Person(name: String, age: Double)
* defined class Person
*
*
*
* Imagine you are calculating the age
values in such as way that occasionally tests
* are failing because of rounding differences that you actually don't care about. For example, you
* expect an age of 29.0, but you're sometimes seeing 29.0001:
*
*
*
* scala> import org.scalautils._
* import org.scalautils._
*
* scala> import TripleEquals._
* import TripleEquals._
*
* scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
* res0: Boolean = false
*
*
*
* The ===
operator looks for an implicit Equality[L]
, where L
is the left-hand type: in this
* case, Person
. Because you didn't specifically provide an implicit Equality[Person]
, ===
will fall back on
* default equality, which will call Person
's equals
method. That equals
method, provided by the Scala compiler
* because Person
is a case class, will declare these two objects unequal because 29.001 does not exactly equal 29.0.
*
*
*
* To make the equality check more forgiving, you could define an implicit Equality[Person]
that compares
* the age
Double
s with a tolerance, like this:
*
*
*
* scala> import Tolerance._
* import Tolerance._
*
* scala> implicit val personEq =
* | new Equality[Person] {
* | def areEqual(a: Person, b: Any): Boolean =
* | b match {
* | case p: Person => a.name == p.name && a.age === p.age +- 0.0002
* | case _ => false
* | }
* | }
* personEq: org.scalautils.Equality[Person] = $anon$1@2b29f6e7
*
*
*
* Now the ===
operator will use your more forgiving Equality[Person]
for the equality check instead
* of default equality:
*
*
*
* scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
* res1: Boolean = true
*
*
*
* Default equality
*
*
* ScalaUtils defines a default Equality[T]
for all types T
whose areEqual
method works by first
* calling .deep
on any passed array, then calling ==
on the left-hand object, passing in the right-hand object.
* You can obtain a default equality via the default
method of the Equality companion object,
* or from the defaultEquality
method defined in TripleEqualsSupport
.
*
*
*
* About equality and equivalence
*
*
* The Equality
trait represents the Java Platform's native notion of equality, as expressed in the signature and contract of
* the equals
method of java.lang.Object
. Essentially, trait Equality
enables you to write alternate
* equals
method implementations for a type outside its defining class.
*
*
*
* In an equals
method, the left-hand type is known to be the type of this
, but
* the right-hand type is Any
.
* As a result, you would normally perform a runtime type test to determine whether the right-hand object is of an appropriate type for equality,
* and if so, compare it structurally for equality with the left-hand (this
) object.
* An an illustration, here's a possible equals
* implementation for the Person
case class shown in the earlier example:
*
*
*
* override def equals(other: Any): Boolean =
* other match {
* case p: Person => name = p.name && age = p.age
* case _ => false
* }
*
*
*
* The areEquals
method of Equality[T]
is similar. The left-hand type is known to be T
, but the right-hand type is Any
, so
* normally you'd need to do a runtime type test in your areEqual
implementation.
* Here's the areEqual
method implementation from the earlier Equality[Person]
example:
*
*
*
* def areEqual(a: Person, b: Any): Boolean =
* b match {
* case p: Person => a.name == p.name && a.age === p.age +- 0.0002
* case _ => false
* }
*
*
* Note: The Equality
type class was inspired in part by the Equal
type class of the
* scalaz
project.
*
*
* @tparam A the type whose equality is being customized
*/
trait Equality[A] extends Equivalence[A] {
/*
*
* The equals
method of java.lang.Object
and areEqual
method of trait Equality
have a similar
* signatures and behavior, and you write them in a similar way.
* When using TypeCheckedTripleEquals
or
* ConversionCheckedTripleEquals
, however,
*
*
*/
/**
* Indicates whether the objects passed as a
and b
are equal.
*
* @param a a left-hand-side object being compared with another (right-hand-side one) for equality (e.g., a == b
)
* @param b a right-hand-side object being compared with another (left-hand-side one) for equality (e.g., a == b
)
* @return true if the passed objects are "equal," as defined by this Equality
instance
*/
def areEqual(a: A, b: Any): Boolean
final def areEquivalent(a: A, b: A): Boolean = areEqual(a, b)
}
object Equality {
def apply[A](uniformity: Uniformity[A]): Equality[A] = {
new NormalizingEquality[A] {
def normalized(a: A): A = uniformity.normalized(a)
def normalizedCanHandle(b: Any): Boolean = uniformity.normalizedCanHandle(b)
def normalizedOrSame(b: Any): Any = uniformity.normalizedOrSame(b)
}
}
/**
* A default Equality
implementation (which can be used for any type) whose
* areEqual
method first calls .deep
on any array (on either the left or right side),
* then compares the resulting objects with ==
.
*/
implicit def default[A]: Equality[A] = new DefaultEquality[A]
}