package scalactic
- Source
- package.scala
- Alphabetic
- By Inheritance
- scalactic
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Type Members
- trait AbstractStringUniformity extends Uniformity[String]
Convenience base trait for string
Uniformitys.Convenience base trait for string
Uniformitys.This trait defines a
normalizedCanHandlemethod that returns true if the passedAnyis aStringand anormalizedOrSamemethod that normalizes any passedStrings via thenormalizedmethod, which is left abstract for subclasses to fill in.Here's an example in which
AbstractStringUniformityis used to normalize strings by ensuring the first character, if any, is capitalized:val capitalized: Uniformity[String] = new AbstractStringUniformity { def normalized(s: String): String = if (s.isEmpty) "" else s.charAt(0).toUpper + s.substring(1) }
Here's an example of using the
capitalizedUniformitywith aMatcherexpression:scala> import org.scalatest._ import org.scalatest._ scala> import Matchers._ import Matchers._ scala> import org.scalactic._ import org.scalactic._ scala> val capitalized: Uniformity[String] = | new AbstractStringUniformity { | def normalized(s: String): String = | if (s.isEmpty) "" else s.charAt(0).toUpper + s.substring(1) | } capitalized: org.scalactic.Uniformity[String] = $anon$1@65601e00 scala> "Hello" should equal ("hello") (after being capitalized) - trait Accumulation extends AccumulationLowPriorityImplicits
Provides mechanisms that enable errors to be accumulated in “accumulating
Ors,”Ors whoseBadtype is anEvery.Provides mechanisms that enable errors to be accumulated in “accumulating
Ors,”Ors whoseBadtype is anEvery.The mechanisms are:
- Passing accumulating
Ors towithGoodmethods - Invoking
combinedon a container of accumulatingOrs - Invoking
validatedByon a container of any type, passing in a function from that type to an accumulatingOr - Invoking
zipon an accumulatingOr - Invoking
whenon an accumulatingOr
For more information and examples, see the Accumulating errors with
Orsection of the main documentation for classOr. - Passing accumulating
- trait AccumulationLowPriorityImplicits extends AnyRef
- final case class Bad[+B](b: B) extends Or[Nothing, B] with Product with Serializable
Contains a “bad” value.
Contains a “bad” value.
You can decide what “bad” means, but it is expected
Badwill be commonly used to hold descriptions of an error (or several, accumulated errors). Some examples of possible error descriptions areStringerror messages,Interror codes,Throwableexceptions, or instances of a case class hierarchy designed to describe errors.- b
the “bad” value
- trait Bool extends AnyRef
A trait that represent a rich-featured boolean value, which includes the following members:
A trait that represent a rich-featured boolean value, which includes the following members:
- a boolean value
- methods useful for failure messages construction
- logical expression methods that makes
Boolcomposable
Boolis used by code generated fromBooleanMacro(whichAssertionsMacroandRequirementsMacrouses), it needs to be public so that the generated code can be compiled. It is expected that ScalaTest users would ever need to useBooldirectly. - abstract class CanEqual[A, B] extends AnyRef
Abstract class used to enforce type constraints for equality checks.
Abstract class used to enforce type constraints for equality checks.
For more information on how this class is used, see the documentation of
TripleEqualsSupport.- Annotations
- @implicitNotFound("types ${A} and ${B} do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[${A},${B}]")
- class Catcher extends AnyRef
Convenience class for extractors that match and return
Throwables based on a type andBooleancondition.Convenience class for extractors that match and return
Throwables based on a type andBooleancondition.Class
Catcherwas motivated by the need to catch and handle exceptions based on more than just the exception's type as a strategy for dealing with "flickering" tests—tests that usually pass, but occasionally fail. The best strategy for dealing with flickers is to fix the test such that they stop flickering, but sometimes that is not practical. In such cases allowing the test to continue flickering can distract the team by requiring them to spend time inspecting failures to determine whether or not they are flickers or real failures that need attention. Worse, with enough flickers, team members can stop checking all failures and not notice real ones.One strategy for dealing with flickers you can't practically fix is to catch exceptions that are causing individual flickers and cancel the test when you detect them. Often this means you will need to insert a catch clause in a particular spot, or a pattern match if in a
withFixture, looking for a particular exception with a particular message or other identifying attribute. If the same problem is causing flickers in many places, it is handy to create an extractor to detect the problem. ThisCatcherclass provides a factory method that takes a partial function fromThrowabletoBooleanand produces such an extractor. Here's an example:val InternalServerError = Catcher { case e: DBAccessException => e.getMessage == "500:Internal Server Error" }
Using this
Catcherin a ScalaTestwithFixturemethod would look like:override def withFixture(test: NoArgTest) = { super.withFixture(test) match { case Failed(InternalServerError(ex)) => Canceled("Canceled because likely a flicker caused by intermittently flaky DB", ex) case other => other } }
- trait Equality[A] extends Equivalence[A]
Defines a custom way to determine equality for a type when compared with another value of type
Any.Defines a custom way to determine equality for a type when compared with another value of type
Any.Equalityenables 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
Doublevalue:scala> case class Person(name: String, age: Double) defined class Person
Imagine you are calculating the
agevalues 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.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> Person("Joe", 29.0001) === Person("Joe", 29.0) res0: Boolean = falseThe
===operator looks for an implicitEquality[L], whereLis the left-hand type: in this case,Person. Because you didn't specifically provide an implicitEquality[Person],===will fall back on default equality, which will callPerson'sequalsmethod. Thatequalsmethod, provided by the Scala compiler becausePersonis 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 theageDoubles 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.scalactic.Equality[Person] = $anon$1@2b29f6e7Now the
===operator will use your more forgivingEquality[Person]for the equality check instead of default equality:scala> Person("Joe", 29.0001) === Person("Joe", 29.0) res1: Boolean = trueDefault equality
Scalactic defines a default
Equality[T]for all typesTwhoseareEqualmethod works by first calling.deepon any passed array, then calling==on the left-hand object, passing in the right-hand object. You can obtain a default equality via thedefaultmethod of the Equality companion object, or from thedefaultEqualitymethod defined inTripleEqualsSupport.About equality and equivalence
The
Equalitytrait represents the Java Platform's native notion of equality, as expressed in the signature and contract of theequalsmethod ofjava.lang.Object. Essentially, traitEqualityenables you to write alternateequalsmethod implementations for a type outside its defining class.In an
equalsmethod, the left-hand type is known to be the type ofthis, but the right-hand type isAny. 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 possibleequalsimplementation for thePersoncase 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
areEqualsmethod ofEquality[T]is similar. The left-hand type is known to beT, but the right-hand type isAny, so normally you'd need to do a runtime type test in yourareEqualimplementation. Here's theareEqualmethod implementation from the earlierEquality[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 }
Equalityis used byTripleEquals, which enforces no type constraint between the left and right values, and theequal,be, andcontainsyntax of ScalaTest Matchers.By contrast,
TypeCheckedTripleEqualsandConversionCheckedTripleEqualsuse anEquivalence.Equivalencediffers fromEqualityin that both the left and right values are of the same type.Equivalenceworks forTypeCheckedTripleEqualsbecause the type constraint enforces that the left type is a subtype or supertype of (or the same type as) the right type, and it widens the subtype to the supertype. So ultimately, both left and right sides are of the supertype type. Similarly,Equivalenceworks forConversionCheckedTripleEqualsbecause the type constraint enforces that an implicit conversion exists from either the left type to the right type, or the right type to the left type, and it always converts one type to the other using the implicit conversion. (If both types are the same type, the identity implicit conversion fromPredefis used.) Because of the conversion, both left and right sides are ultimately of the converted-to type. Here's an example of how writing anEquivalence'sareEquivalentmethod might look:def areEquivalent(a: Person, b: Person): Boolean = a.name == b.name && a.age === b.age +- 0.0002
Scalactic provides both
EqualityandEquivalencebecause theAnyinEqualitycan sometimes make things painful. For example, in traitTolerantNumerics, a single generic factory method can produceEquivalences for anyNumerictype, but because of theAny, a separate factory method must be defined to produce anEqualityfor eachNumerictype.If you just want to customize the notion of equality for
===used inBooleanexpressions, you can work withEquivalences instead ofEqualitys. If you do chose to write the more generalEqualitys, they can be used wherever anEquivalenceis required, becauseEqualityextendsEquivalence, defining a final implementation ofareEquivalentthat invokesareEqual.Note: The
Equalitytype class was inspired in part by theEqualtype class of thescalazproject.- A
the type whose equality is being customized
- trait Equivalence[T] extends AnyRef
Defines a custom way to determine equality for a type when compared with another value of the same type.
Defines a custom way to determine equality for a type when compared with another value of the same type.
Equivalenceenables you to define alternate notions of equality for types that can be used with ScalaUtil'sTypeCheckedTripleEqualsandConversionCheckedTripleEqualstraits. These traits can be used to perform equality comparisons with type constraints enforced at compile time using ScalaUtil's===and!==syntax and ScalaTest'sshould===syntax ofMatcherstrait.Because
EqualityextendsEquivalence, you automatically define anEquivalence[T]when you define anEquality[T]. Most often you will usually want to define customEqualitys, because they will be more generally useful: they are also used by Scalactic'sTripleEqualstrait and ScalaTest'sequal,be, andcontainmatcher syntax. However, if you really want just anEquivalence, and writing anEqualityis inconvenient, you can write anEquivalencedirectly for a type.For example, say you have a case class that includes a
Doublevalue:scala> case class Person(name: String, age: Double) defined class Person
Imagine you are calculating the
agevalues 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.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> Person("Joe", 29.0001) === Person("Joe", 29.0) res0: Boolean = falseThe
===operator ofTypeCheckedTripleEqualslooks for an implicitEquivalence[SUPER], whereSUPERis either the left-hand or right-hand type, whichever one is a supertype of the other. In this case, both sides arePerson(which is considered a supertype of itself), so the compiler will look for anEquivalence[Person]. Because you didn't specifically provide an implicitEquivalence[Person],===will fall back on default equality, because anEquality[Person]is-anEquivalence[Person]. The defaultEquality[Person]will callPerson'sequalsmethod. Thatequalsmethod, provided by the Scala compiler becausePersonis 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
Equivalence[Person]that compares theageDoubles with a tolerance, like this:scala> import Tolerance._ import Tolerance._ scala> implicit val personEq = | new Equivalence[Person] { | def areEquivalent(a: Person, b: Person): Boolean = | a.name == b.name && a.age === b.age +- 0.0002 | } personEq: org.scalactic.Equivalence[Person] = $anon$1@7892bd8Now the
===operator will use your more forgivingEquivalence[Person]for the equality check instead of default equality:scala> Person("Joe", 29.0001) === Person("Joe", 29.0) res1: Boolean = true - type ErrorMessage = String
Type alias for
String. - sealed abstract class Every[+T] extends PartialFunction[Int, T] with Product with Serializable
An ordered, immutable, non-empty collection of elements.
An ordered, immutable, non-empty collection of elements.
Class
Everyhas two and only two subtypes:OneandMany. AOnecontains exactly one element. AManycontains two or more elements. Thus no way exists for anEveryto contain zero elements.Constructing
EverysYou can construct an
Everyby passing one or more elements to theEvery.applyfactory method:Every(1) Every(1, 2) Every(1, 2, 3)
Alternatively you can pass one element to the
One.applyfactory method, or two or more elements toMany.apply:One(1) Many(1, 3) Many(1, 2, 3)
Working with
EverysEverydoes not extend Scala'sSeqorTraversabletraits because these require that implementations may be empty. For example, if you invoketailon aSeqthat contains just one element, you'll get an emptySeq:scala> List(1).tail res6: List[Int] = List()
On the other hand, many useful methods exist on
Seqthat when invoked on a non-emptySeqare guaranteed to not result in an emptySeq. For convenience,Everydefines a method corresponding to every suchSeqmethod. Here are some examples:Many(1, 2, 3).map(_ + 1) // Result: Many(2, 3, 4) One(1).map(_ + 1) // Result: One(2) Every(1, 2, 3).containsSlice(Every(2, 3)) // Result: true Every(1, 2, 3).containsSlice(Every(3, 4)) // Result: false Every(-1, -2, 3, 4, 5).minBy(_.abs) // Result: -1
Everydoes not currently define any methods corresponding toSeqmethods that could result in an emptySeq. However, an implicit converison fromEverytocollection.immutable.IndexedSeqis defined in theEverycompanion object that will be applied if you attempt to call one of the missing methods. As a result, you can invokefilteron anEvery, even thoughfiltercould result in an empty sequence—but the result type will becollection.immutable.IndexedSeqinstead ofEvery:Every(1, 2, 3).filter(_ < 10) // Result: Vector(1, 2, 3) Every(1, 2, 3).filter(_ > 10) // Result: Vector()
You can use
Everys inforexpressions. The result will be anEveryunless you use a filter (anifclause). Because filters are desugared to invocations offilter, the result type will switch to acollection.immutable.IndexedSeqat that point. Here are some examples:scala> import org.scalactic._ import org.scalactic._ scala> for (i <- Every(1, 2, 3)) yield i + 1 res0: org.scalactic.Every[Int] = Many(2, 3, 4) scala> for (i <- Every(1, 2, 3) if i < 10) yield i + 1 res1: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4) scala> for { | i <- Every(1, 2, 3) | j <- Every('a', 'b', 'c') | } yield (i, j) res3: org.scalactic.Every[(Int, Char)] = Many((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c)) scala> for { | i <- Every(1, 2, 3) if i < 10 | j <- Every('a', 'b', 'c') | } yield (i, j) res6: scala.collection.immutable.IndexedSeq[(Int, Char)] = Vector((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))Motivation for
EverysAlthough
Everyis a general-purpose, non-empty ordered collection, it was motivated by the desire to enable easy accumulation of errors inOrs. For examples ofEveryused in that use case, see the Accumulating errors withOrsection in the main documentation forOr.- T
the type of elements contained in this
Every
- trait Explicitly extends AnyRef
Provides ScalaTest's “explicitly DSL,” which facilitates the explicit specification of an
Equality[T]or aUniformity[T]whereEquality[T]is taken implicitly.Provides ScalaTest's “explicitly DSL,” which facilitates the explicit specification of an
Equality[T]or aUniformity[T]whereEquality[T]is taken implicitly.The Explicitly DSL can be used with the
===and!==operators of Scalactic as well as theshouldequal,be,contain, and===syntax of ScalaTest matchers.If you want to customize equality for a type in general, you would likely want to place an implicit
Equality[T]for that type in scope (or inT's companion object). That implicit equality definition will then be picked up and used when that type is compared for equality with theequal,be, andcontainmatchers in ScalaTest tests and with===in both tests and production code. If you just want to use a custom equality for a single comparison, however, you may prefer to pass it explicitly. For example, if you have an implicitEquality[String]in scope, you can force a comparison to use the default equality with this syntax:// In production code: if ((result === "hello")(decided by defaultEquality)) true else false
// In tests: result should equal ("hello") (decided by defaultEquality)The explicitly DSL also provides support for specifying a one-off equality that is based on a normalization. For example, Scalactic offers a
StringNormalizationstrait that provides methods such astrimmedandlowerCasedthat returnNormalization[String]instances that normalize by trimming and lower-casing, respectively. If you bring those into scope by mixing in or importing the members ofStringNormalizations, you could use the explicitly DSL like this:// In production code: if ((result === "hello")(after being lowerCased)) true else false
// In tests: result should equal ("hello") (after being lowerCased and trimmed)If you prefer not to use English-like DSLs in your production code, you can alternatively not use the
Explicitlytrait and instead write:// To explicitly specify an Equality instance, just specify it: if ((result === "hello")(Equality.default)) true else false
// To base an Equality instance on a Uniformity, just // call toEquality on it: if ((result === "hello")(lowerCased.toEquality)) true else false - case class Fail[E](error: E) extends Validation[E] with Product with Serializable
Indicates a validation failed, describing the failure with a contained error value.
Indicates a validation failed, describing the failure with a contained error value.
- E
the type of value describing a validation failure for this
Fail- error
an error value describing the validation failure
- trait FutureSugar extends AnyRef
Trait providing an implicit class that adds a
validatingmethod toFuture, which takes one or more validation functions and returns either the sameFutureif either theFuturehad already failed or its value passes all the functions, orValidationFailedExceptioncontaining an error message describing the first validation that failed.Trait providing an implicit class that adds a
validatingmethod toFuture, which takes one or more validation functions and returns either the sameFutureif either theFuturehad already failed or its value passes all the functions, orValidationFailedExceptioncontaining an error message describing the first validation that failed.Here's an example validation method, which passes if the given
Intis evenly divisible by 10 (i.e., the result will bePass). If the value does not pass this test, the result is aFailcontaining a helpful error message string.scala> import org.scalactic._ import org.scalactic._ scala> import FutureSugar._ import org.scalactic.FutureSugar._ scala> import scala.concurrent.Future import scala.concurrent.Future scala> import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global scala> def isRound(i: Int): Validation[ErrorMessage] = | if (i % 10 == 0) Pass else Fail(i + " was not a round number") isRound: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage]Validation will be attempted on a successful
Try. If the validation succeeds, the resultingFuturewill be the same successfulFuturewith the same value. (A "validation" only transforms theFutureif the validation fails, otherwise it is the sameFuture. The only difference is its value has now been proven valid.) In the following example, a successfulFuture[Int]with the value 100 passes the validation (which checks whether 100 is evenly divisible by 10), therefore the result of thevalidatingcall is the same successfulFuturewith the same value.scala> val fut100 = Future(100) fut100: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@67f9c9c6 scala> fut100.value res0: Option[scala.util.Try[Int]] = Some(Success(100)) scala> val round100 = fut100.validating(isRound) round100: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@1ac2f0d1 scala> round100.value res1: Option[scala.util.Try[Int]] = Some(Success(100))
If validation fails, the successful
Futurewill be transformed into a failed one, with aValidationFailedExceptionthat contains the error message returned by the validation function. In the following example, 42 fails the validation because it is not evenly divisible by 10:scala> val fut42 = Future(42) fut42: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@19c6e4d1 scala> fut42.value res2: Option[scala.util.Try[Int]] = Some(Success(42)) scala> val round42 = fut42.validating(isRound) round42: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@b5175d scala> round42.value res3: Option[scala.util.Try[Int]] = Some(Failure(org.scalactic.exceptions.ValidationFailedException: 42 was not a round number))
If
validatingis called on a failedFuture, it just returns the same failedFuture:scala> val futEx = Future[Int] { throw new Exception("oops!") } futEx: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@3ba0299c scala> futEx.value res4: Option[scala.util.Try[Int]] = Some(Failure(java.lang.Exception: oops!)) scala> val roundEx = futEx.validating(isRound) roundEx: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@22bf1acf scala> roundEx.value res5: Option[scala.util.Try[Int]] = Some(Failure(java.lang.Exception: oops!))The
validatingmethod accepts one or more validation functions. If you pass more than one, they will be tried in order up until the first failure, whose error message will appear in theValidationFailedException. In other words,validatingwill short circuit at the first error and return that. It will not accumulate errors. For example, the following validation will short circuit after theisDivBy3function fails:scala> def isDivBy3(i: Int): Validation[ErrorMessage] = | if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3") isDivBy3: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage] scala> def isAnswerToLifeTheUniverseAndEverything(i: Int): Validation[ErrorMessage] = | if (i == 42) Pass else Fail(i + " did not equal 42") isAnswerToLifeTheUniverseAndEverything: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage] scala> val futShort = fut100.validating(isRound, isDivBy3, isAnswerToLifeTheUniverseAndEverything) futShort: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@30bb943e scala> futShort.value res11: Option[scala.util.Try[Int]] = Some(Failure(org.scalactic.exceptions.ValidationFailedException: 100 was not divisible by 3)) - final case class Good[+G](g: G) extends Or[G, Nothing] with Product with Serializable
Contains a “good” value.
Contains a “good” value.
You can decide what “good” means, but it is expected
Goodwill be commonly used to hold valid results for processes that may fail with an error instead of producing a valid result.- g
the “good” value
- trait LowPriorityTypeCheckedConstraint extends TripleEqualsSupport
Provides an implicit conversion that will be applied only if a higher-priority implicit conversion declared a subtrait is not applicable.
Provides an implicit conversion that will be applied only if a higher-priority implicit conversion declared a subtrait is not applicable.
The purpose of this trait is to make the
===operator symetric. In other words, a===invocation will be allowed if subtype relationship exists in either direction. For example, in the following expression, the left hand side is a subtype of the right hand side:List(1, 2, 3) === Seq(1, 2, 3)
But in the next expression, it the right hand side is a subtype of the left hand side
Seq(1, 2, 3) === List(1, 2, 3)
The first expression above is enabled by the implicit conversion
typeCheckedConstraintin traitTypeCheckedTripleEquals. The second expression above is enabled by the implicit conversionlowPriorityTypeCheckedConstraintin this trait.The reason these two implicit methods aren't both declared in the subtraits is that if the subtype relationship existed in both directions, they would conflict. This can happen when the exact same type is on both the left and right hand sides, because a type is a subtype of itself. By placing one of them in this supertrait, the higher priority conversion will be selected.
- final case class Many[+T](firstElement: T, secondElement: T, otherElements: T*) extends Every[T] with Product with Serializable
An
Everythat contains two or more elements.An
Everythat contains two or more elements.For more information and examples, see the main documentation for superclass
Every.- T
the type of the element contained in this
Many- firstElement
the first element (with index 0) contained in this
Many- secondElement
the second element (with index 1) contained in this
Many- otherElements
a varargs of zero or more other elements (with index 2, 3, ...) contained in this
Many
- trait MapEqualityConstraints extends AnyRef
Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaMaps to one that more closely matches Scala's approach toMapequality.Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaMaps to one that more closely matches Scala's approach toMapequality.Scala's approach to
Mapequality is that if both objects being compared areMaps, the elements are compared to determine equality. This means you could compare an immutableTreeMapand a mutableHashMapfor equality, for instance, and get true so long as the two maps contained the same key-value mappings. Here's an example:scala> import scala.collection.immutable.TreeMap import scala.collection.immutable.TreeMap scala> import scala.collection.mutable.HashMap import scala.collection.mutable.HashMap scala> TreeMap("one" -> 1, "two" -> 2) == HashMap("one" -> 1, "two" -> 2) res0: Boolean = trueSuch a comparison would not, however, compile if you used
===under eitherTypeCheckedTripleEqualsorConversionCheckedTripleEquals, becauseTreeMapandHashMapare not in a subtype/supertype relationship, nor does an implicit conversion by default exist between them:scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> TreeMap("one" -> 1, "two" -> 2) === HashMap("one" -> 1, "two" -> 2) <console>:16: error: types scala.collection.immutable.TreeMap[String,Int] and scala.collection.mutable.HashMap[String,Int] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.TreeMap[String,Int], scala.collection.mutable.HashMap[String,Int]] TreeMap("one" -> 1, "two" -> 2) === HashMap("one" -> 1, "two" -> 2) ^If you mix or import the implicit conversion provided by
MapEqualityConstraint, however, the comparison will be allowed:scala> import MapEqualityConstraints._ import MapEqualityConstraints._ scala> TreeMap("one" -> 1, "two" -> 2) === HashMap("one" -> 1, "two" -> 2) res2: Boolean = trueThe equality constraint provided by this trait requires that both left and right sides are subclasses of
scala.collection.GenMapand that anEqualityConstraintcan be found for both key types and both value types. In the example above, both theTreeMapandHashMapare subclasses ofscala.collection.GenMap, and the regularTypeCheckedTripleEqualsprovides equality constraints for the key types, both of which areString, and value types, both of which areInt. By contrast, this trait would not allow aTreeMap[String, Int]to be compared against aHashMap[String, java.util.Date], because no equality constraint will exist between the value typesIntandDate:scala> import java.util.Date import java.util.Date scala> TreeMap("one" -> 1, "two" -> 2) === HashMap("one" -> new Date, "two" -> new Date) <console>:20: error: types scala.collection.immutable.TreeMap[String,Int] and scala.collection.mutable.HashMap[String,java.util.Date] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.TreeMap[String,Int], scala.collection.mutable.HashMap[String,java.util.Date]] TreeMap("one" -> 1, "two" -> 2) === HashMap("one" -> new Date, "two" -> new Date) ^ - trait NormMethods extends AnyRef
Provides an implicit conversion that allows
normto be invoked on any value of typeTfor which an implicitNormalization[T]exists.Provides an implicit conversion that allows
normto be invoked on any value of typeTfor which an implicitNormalization[T]exists.Here's an example:
scala> import org.scalactic._ import org.scalactic._ scala> import StringNormalizations._ import StringNormalizations._ scala> implicit val stringNormalization = lowerCased and trimmed stringNormalization: org.scalactic.Uniformity[String] = org.scalactic.Uniformity$$anon$1@19ba67ec scala> import NormMethods._ import NormMethods._ scala> val s = " There " s: String = " There " scala> "Hey " + s + "!" res5: String = Hey There ! scala> "Hey " + s.norm + "!" res6: String = Hey there!
- trait Normalization[A] extends AnyRef
Defines a custom way to normalize instances of a type.
Defines a custom way to normalize instances of a type.
For example, to normalize
Doubles by truncating off any decimal part, you might write:import org.scalactic._
val truncated = new Normalization[Double] { def normalized(d: Double) = d.floor }Given this definition you could use it with the
ExplicitlyDSL like this:import org.scalatest._ import Matchers._ import TypeCheckedTripleEquals._
(2.1 should === (2.0)) (after being truncated)Note that to use a
Normalizationwith theExplicitlyDSL, you'll need to useTypeCheckedTripleEquals. If you're just using plain-oldTripleEquals, you'll need aUniformity, aNormalizationsubclass.If you make the
truncatedvalimplicit and import or mix in the members ofNormMethods, you can access the behavior by invoking.normonDoubles.implicit val doubleNormalization = truncated import NormMethods._
val d = 2.1 d.norm // returns 2.0- A
the type whose normalization is being defined
- trait NormalizingEquality[A] extends Equality[A]
An
Equality[A]implementation that determines the equality of two objects by normalizing one or both objects, then comparing the results using an “after normalization” equality referenced from theafterNormalizationEqualitymember.An
Equality[A]implementation that determines the equality of two objects by normalizing one or both objects, then comparing the results using an “after normalization” equality referenced from theafterNormalizationEqualitymember. By default, theafterNormalizationEqualityis an instance ofEquality.default[A].NormalizingEqualityis returned by theExplicitlyDSL's “afterbeing” syntax, using for theafterNormalizationEqualitythe implicitEqualityin scope for the type ofUniformitypassed tobeing. Here's an example:scala> import org.scalactic._ import org.scalactic._ scala> import Explicitly._ import Explicitly._ scala> import StringNormalizations._ import StringNormalizations._ scala> after being lowerCased res0: org.scalactic.NormalizingEquality[String] = ComposedNormalizingEquality(Equality.default,lowerCased)
- trait NormalizingEquivalence[A] extends Equivalence[A]
An
Equivalence[A]implementation that determines the equality of two objects by normalizing one or both objects, then comparing the results using an “after normalization”Equivalencereferenced from theafterNormalizationEquivalencemember.An
Equivalence[A]implementation that determines the equality of two objects by normalizing one or both objects, then comparing the results using an “after normalization”Equivalencereferenced from theafterNormalizationEquivalencemember. By default, theafterNormalizationEquivalenceis an instance ofEquivalence.default[A].NormalizingEquivalenceis returned by theExplicitlyDSL's “afterbeing” syntax, using for theafterNormalizationEquivalencethe implicitEquivalencein scope for the type ofNormalizationpassed tobeing. Here's an example:scala> import org.scalactic._ import org.scalactic._ scala> import Explicitly._ import Explicitly._ scala> val lowerCased: Normalization[String] = StringNormalizations.lowerCased lowerCased: org.scalactic.Normalization[String] = lowerCased scala> after being lowerCased res0: org.scalactic.NormalizingEquivalence[String] = ComposedNormalizingEquivalence(Equality.default,lowerCased)
- final case class One[+T](loneElement: T) extends Every[T] with Product with Serializable
An
Everythat contains exactly one element.An
Everythat contains exactly one element.For more information and examples, see the main documentation for superclass
Every.- T
the type of the element contained in this
One- loneElement
the lone element contained in this
One
- trait OptionSugar extends AnyRef
Trait providing an implicit class that adds a
toOrmethod toOption, which convertsSometoGood,NonetoBad.Trait providing an implicit class that adds a
toOrmethod toOption, which convertsSometoGood,NonetoBad.You can use the
toOrmethod to record information about why a processing of nestedOptions resulted inNone. For example, the followingforexpression results inNoneif either the passed optionalPersonisNoneor else if the contained optional age isNone:scala> case class Person(name: String, age: Option[Int]) defined class Person scala> def ageOf(person: Option[Person]) = | for { | per <- person | age <- per.age | } yield age ageOf: (person: Option[Person])Option[Int] scala> ageOf(Some(Person("Ralph", Some(32)))) res0: Option[Int] = Some(32) scala> ageOf(Some(Person("Curt", None))) res3: Option[Int] = None scala> ageOf(None) res2: Option[Int] = NoneIf you instead populate the
forexpression withOrs, supplying an error message or other "bad" value to thetoOrmethod in case ofNone, you'll get an indication of which part failed if aNoneis encountered:scala> import OptionSugar._ import OptionSugar._ scala> def ageOf(person: Option[Person]) = | for { | per <- person toOr "no person here" | age <- per.age toOr "ageless person" | } yield age ageOf: (person: Option[Person])org.scalactic.Or[Int,String] scala> ageOf(Some(Person("Ralph", Some(32)))) res1: org.scalactic.Or[Int,String] = Good(32) scala> ageOf(Some(Person("Curt", None))) res2: org.scalactic.Or[Int,String] = Bad(ageless person) scala> ageOf(None) res3: org.scalactic.Or[Int,String] = Bad(no person here) - sealed abstract class Or[+G, +B] extends Product with Serializable
Represents a value that is one of two possible types, with one type being “good” and the other “bad.”
Represents a value that is one of two possible types, with one type being “good” and the other “bad.”
An
Orwill either be a “good” value wrapped in an instance ofGoodor a “bad” value wrapped in an instance ofBad.The motivation for
OrOrdiffers from Scala'sEithertype in thatEithertreats both itsLeftandRightalternatives in an identical manner, whereasOrtreats its two alternatives differently: it favorsGoodoverBad. Because of this, it is more convenient to work withOrs when you prefer one alternative over the other; for example, if one alternative represents a valid result and another represents an error.To illustrate, imagine you want to create instances this
Personclass from user input strings:case class Person(name: String, age: Int)
You might write a method that parses the name from user input string and returns an
Option[String]:Noneif the string is empty or blank, else the trimmed string wrapped in aSome:def parseName(input: String): Option[String] = { val trimmed = input.trim if (!trimmed.isEmpty) Some(trimmed) else None }
You might also write a method that parses the age from user input string and returns an
Option[Int]:Noneif either the string is not a valid integer or it is a negative integer, else the string converted to an integer wrapped in aSome:def parseAge(input: String): Option[Int] = { try { val age = input.trim.toInt if (age >= 0) Some(age) else None } catch { case _: NumberFormatException => None } }
With these building blocks you could write a method that parses name and age input strings and returns either a
Person, wrapped in aSome, orNoneif either the name or age, or both, was invalid:def parsePerson(inputName: String, inputAge: String): Option[Person] = for { name <- parseName(inputName) age <- parseAge(inputAge) } yield Person(name, age)
Here are some examples of invoking
parsePerson:parsePerson("Bridget Jones", "29") // Result: Some(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "") // Result: None
parsePerson("Bridget Jones", "-29") // Result: None
parsePerson("", "") // Result: NoneNow imagine you want to give an error message back if the user's input is invalid. You might rewrite the parsing methods to return an
Eitherinstead. In this case, the desired result is a valid name or age, which by convention should be placed on the right of theEither. The left will be aStringerror message. Here's the newparseNamefunction, which returns anEither[String, String]:def parseName(input: String): Either[String, String] = { val trimmed = input.trim if (!trimmed.isEmpty) Right(trimmed) else Left(s""""${input}" is not a valid name""") }
And here's the new
parseAgefunction, which returns anEither[String, Int]:def parseAge(input: String): Either[String, Int] = { try { val age = input.trim.toInt if (age >= 0) Right(age) else Left(s""""${age}" is not a valid age""") } catch { case _: NumberFormatException => Left(s""""${input}" is not a valid integer""") } }
The new
parsePersonmethod will return anEither[String, Person]:def parsePerson(inputName: String, inputAge: String): Either[String, Person] = for { name <- parseName(inputName).right age <- parseAge(inputAge).right } yield Person(name, age)
Note that
Eitherrequires you to add.rightat the end of each generator in theforexpression. Although the convention is to place the valid result on the right, you must explicitly (and repetitively) indicate that you've done so by transforming theEitherto aRightProjectionby invoking.rightat each step. Given this implementation, theparsePersonmethod will now short-circuit at the first sign of trouble (as it did when we used anOption), but you now get the first error message returned in aLeft. Here are some examples:parsePerson("Bridget Jones", "29") // Result: Right(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "") // Result: Left("" is not a valid integer)
parsePerson("Bridget Jones", "-29") // Result: Left("-29" is not a valid age)
parsePerson("", "") // Result: Left("" is not a valid name)An
Eitherwith “attitude”Because
Ordeclares one alternative to be “good” and the other “bad,” it is more convenient thanEitherin this kind of situation. One difference to note withOris that theGoodalternative is on the left,Badon the right. The reason is thatOris designed to be written using infix notation, and placing the “happy path” first is more readable. For example, instead of writing:Or[Int, ErrorMessage]You can write:
Int Or ErrorMessage
Here's how the
parseNamemethod might be written using anOr, whereErrorMessageis a type alias forStringdeclared in theorg.scalacticpackage object:import org.scalactic._
def parseName(input: String): String Or ErrorMessage = { val trimmed = input.trim if (!trimmed.isEmpty) Good(trimmed) else Bad(s""""${input}" is not a valid name""") }You can think of the
StringOrErrorMessageresult type like this:The
parseNamemethod will return a nameStringor, if the input string is not a valid name, anErrorMessage.Here's how the
parseAgemethod might be written:def parseAge(input: String): Int Or ErrorMessage = { try { val age = input.trim.toInt if (age >= 0) Good(age) else Bad(s""""${age}" is not a valid age""") } catch { case _: NumberFormatException => Bad(s""""${input}" is not a valid integer""") } }
Given these implementations, here's how you'd write the
parsePersonmethod:def parsePerson(inputName: String, inputAge: String): Person Or ErrorMessage = for { name <- parseName(inputName) age <- parseAge(inputAge) } yield Person(name, age)
Because of
Or's attitude, you need not write.goodat the end of each generator.Orwill keep going so long as each step produces aGood, short circuiting at the first sign of aBad. Here are a few invocations of thisparsePersonmethod:parsePerson("Bridget Jones", "29") // Result: Good(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "") // Result: Bad("" is not a valid integer)
parsePerson("Bridget Jones", "-29") // Result: Bad("-29" is not a valid age)
parsePerson("", "") // Result: Bad("" is not a valid name)Accumulating errors with
OrAnother difference between
OrandEitheris thatOrenables you to accumulate errors if theBadtype is anEvery. AnEveryis similar to aSeqin that it contains ordered elements, but different fromSeqin that it cannot be empty. AnEveryis either aOne, which contains one and only one element, or aMany, which contains two or more elements.Note: an
OrwhoseBadtype is anEvery, or one of its subtypes, is called an “accumulatingOr.”To rewrite the previous example so that errors can be accumulated, you need first to return an
Everyas theBadtype. Here's how you'd change theparseNamemethod:def parseName(input: String): String Or One[ErrorMessage] = { val trimmed = input.trim if (!trimmed.isEmpty) Good(trimmed) else Bad(One(s""""${input}" is not a valid name""")) }
Because
parseNamewill either return a valid nameStringwrapped in aGood, or one error message, wrapped in aBad, you would write theBadtype asOne[ErrorMessage]. The same is true forparseAge:def parseAge(input: String): Int Or One[ErrorMessage] = { try { val age = input.trim.toInt if (age >= 0) Good(age) else Bad(One(s""""${age}" is not a valid age""")) } catch { case _: NumberFormatException => Bad(One(s""""${input}" is not a valid integer""")) } }
Because a
forexpression short-circuits on the firstBadencountered, you'll need to use a different approach to write theparsePersonmethod. In this example, thewithGoodmethod from traitAccumulationwill do the trick:import Accumulation._
def parsePerson(inputName: String, inputAge: String): Person Or Every[ErrorMessage] = { val name = parseName(inputName) val age = parseAge(inputAge) withGood(name, age) { Person(_, _) } }Trait
Accumulationoffers overloadedwithGoodmethods that take 1 to 22 accumulatingOrs, plus a function taking the same number of correspondingGoodvalues. In this example, if bothnameandageareGoods, thewithGoodmethod will pass the good nameStringand ageIntto thePerson(_, _)function, and return the resultingPersonobject wrapped in aGood. If eithernameandage, or both, areBad,withGoodwill return the accumulated errors in aBad.The result of
parsePerson, ifBad, will therefore contain either one or two error messages, i.e., the result will either be aOneor aMany. As a result, the result type ofparsePersonmust bePersonOrEvery[ErrorMessage]. Regardless of whether aBadresult contains one or two error messages, it will contain every error message. Here's some invocations of this accumulating version ofparsePerson:parsePerson("Bridget Jones", "29") // Result: Good(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "") // Result: Bad(One("" is not a valid integer))
parsePerson("Bridget Jones", "-29") // Result: Bad(One("-29" is not a valid age))
parsePerson("", "") // Result: Bad(Many("" is not a valid name, "" is not a valid integer))Note that in the last example, the
Badcontains an error message for both name and age.Other ways to accumulate errors
The
Accumlationtrait also enables other ways of accumulating errors.Using
combinedIf you have a collection of accumulating
Ors, for example, you can combine them into oneOrusingcombined, like this:List(parseAge("29"), parseAge("30"), parseAge("31")).combined // Result: Good(List(29, 30, 31))
List(parseAge("29"), parseAge("-30"), parseAge("31")).combined // Result: Bad(One("-30" is not a valid age))
List(parseAge("29"), parseAge("-30"), parseAge("-31")).combined // Result: Bad(Many("-30" is not a valid age, "-31" is not a valid age))Using
validatedByOr if you have a collection of values and a function that transforms that type of value into an accumulating
Ors, you can validate the values using the function usingvalidatedBy, like this:List("29", "30", "31").validatedBy(parseAge) // Result: Good(List(29, 30, 31))
List("29", "-30", "31").validatedBy(parseAge) // Result: Bad(One("-30" is not a valid age))
List("29", "-30", "-31").validatedBy(parseAge) // Result: Bad(Many("-30" is not a valid age, "-31" is not a valid age))Using
zipYou can also zip two accumulating
Ors together. If both areGood, you'll get aGoodtuple containin both originalGoodvalues. Otherwise, you'll get aBadcontaining every error message. Here are some examples:parseName("Dude") zip parseAge("21") // Result: Good((Dude,21))
parseName("Dude") zip parseAge("-21") // Result: Bad(One("-21" is not a valid age))
parseName("") zip parseAge("-21") // Result: Bad(Many("" is not a valid name, "-21" is not a valid age))Using
whenIn addition, given an accumlating
Or, you can pass one or more validation functions towhenon theOrto submit thatOrto further scrutiny. A validation function accepts aGoodtype and returns aValidation[E], whereEis the type in theEveryin theBadtype. For anIntOrOne[ErrorMessage], for example the validation function type would beInt=>Validation[ErrorMessage]. Here are a few examples:def isRound(i: Int): Validation[ErrorMessage] = if (i % 10 == 0) Pass else Fail(i + " was not a round number")
def isDivBy3(i: Int): Validation[ErrorMessage] = if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3")If the
Oron which you callwhenis alreadyBad, you get the same (Bad)Orback, because noGoodvalue exists to pass to the valiation functions:parseAge("-30").when(isRound, isDivBy3) // Result: Bad(One("-30" is not a valid age))
If the
Oron which you callwhenisGood, and also passes all the validation functions (i.e., the all returnNone), you again get the sameOrback, but this time, aGoodone:parseAge("30").when(isRound, isDivBy3) // Result: Good(30)
If one or more of the validation functions fails, however, you'll get a
Badback contining every error. Here are some examples:parseAge("33").when(isRound, isDivBy3) // Result: Bad(One(33 was not a round number))
parseAge("20").when(isRound, isDivBy3) // Result: Bad(One(20 was not divisible by 3))
parseAge("31").when(isRound, isDivBy3) // Result: Bad(Many(31 was not a round number, 31 was not divisible by 3))Note that you can use
whento accumulate errors in aforexpression involving an accumulatingOr, like this:for (age <- parseAge("-30") when (isRound, isDivBy3)) yield age // Result: Bad(One("-30" is not a valid age))
for (age <- parseAge("30") when (isRound, isDivBy3)) yield age // Result: Good(30)
for (age <- parseAge("33") when (isRound, isDivBy3)) yield age // Result: Bad(One(33 was not a round number))
for (age <- parseAge("20") when (isRound, isDivBy3)) yield age // Result: Bad(One(20 was not divisible by 3))
for (age <- parseAge("31") when (isRound, isDivBy3)) yield age // Result: Bad(Many(31 was not a round number, 31 was not divisible by 3))Much ado about
NothingBecause
Orhas two types, but each of its two subtypes only takes a value of one or the other type, the Scala compiler will inferNothingfor the unspecified type:scala> Good(3) res0: org.scalactic.Good[Int,Nothing] = Good(3) scala> Bad("oops") res1: org.scalactic.Bad[Nothing,String] = Bad(oops)Often
Nothingwill work fine, as it will be widened as soon as the compiler encounters a more specific type. Sometimes, however, you may need to specify it. In such situations you can use this syntax:scala> Good(3).orBad[String] res2: org.scalactic.Good[Int,String] = Good(3) scala> Good[Int].orBad("oops") res3: org.scalactic.Bad[Int,String] = Bad(oops)If you want to specify both types, because you don't like the inferred type, you can do so like this:
scala> Good[AnyVal, String](3) res4: org.scalactic.Good[AnyVal,String] = Good(3) scala> Bad[Int, ErrorMessage]("oops") res5: org.scalactic.Bad[Int,org.scalactic.ErrorMessage] = Bad(oops)But you may find the code is clearer if you instead use a type ascription, like this:
scala> Good(3): AnyVal Or String res6: org.scalactic.Or[AnyVal,String] = Good(3) scala> Bad("oops"): Int Or ErrorMessage res7: org.scalactic.Or[Int,org.scalactic.ErrorMessage] = Bad(oops)Note: The
Orhierarchy was inspired in part by the disjoint union (\/) andValidationtypes ofscalaz, theProcessResulttype of Typesafe Activator, and theResulttype of ScalaKittens. - trait Prettifier extends Serializable
A function that given any object will produce a “pretty” string representation of that object, where “pretty” is in the eye of the implementer.
A function that given any object will produce a “pretty” string representation of that object, where “pretty” is in the eye of the implementer.
Scala's
Anytype declares atoStringthat will convert any object to aStringrepresentation. ThisStringrepresentation is primarily intended for programmers, and is usually sufficient. However, sometimes it can be helpful to provide an alternative implementation oftoStringfor certain types. For example, thetoStringimplementation onStringprints out the value of theString:scala> "1".toString res0: String = 1
If the error message that resulted from comparing
Int1 withString"1"in a ScalaTest assertion usedtoString, therefore, the error message would be:1 did not equal 1
To make it quicker to figure out why the assertion failed, ScalaTest prettifies the objects involved in the error message. The default
Prettifierwill place double quotes on either side of aStringstoStringresult:scala> import org.scalactic._ import org.scalactic._ scala> Prettifier.default("1") res1: String = "1"Thus the error message resulting from comparing
Int1 withString"1", in a ScalaTest assertion is:1 did not equal "1"
If you wish to prettify an object in production code, for example, to issue a profoundly clear debug message, you can use
PrettyMethodsand invokepretty. Here's an example:scala> import PrettyMethods._ import PrettyMethods._ scala> 1.pretty res2: String = 1 scala> "1".pretty res3: String = "1"
For example, the default
Prettifier, `Prettifier.default`, transforms:Nullto:nullUnitto:<() the Unit value>Stringto:"string"(thetoStringresult surrounded by double quotes)Charto:'c'(thetoStringresult surrounded by single quotes)Arrayto:Array("1", "2", "3")scala.Someto:Some("3")scala.util.Leftto:Left("3")scala.util.Rightto:Right("3")scala.util.Successto:Success("3")org.scalactic.Goodto:Good("3")org.scalactic.Badto:Bad("3")org.scalactic.Oneto:One("3")org.scalactic.Manyto:Many("1", "2", "3")scala.collection.GenTraversableto:List("1", "2", "3")java.util.Collectionto:["1", "2", "3"]java.util.Mapto:{1="one", 2="two", 3="three"}
For anything else, the default
Prettifierreturns the result of invokingtoString.Note:
Prettifieris not parameterized (i.e.,Prettifier[T], whereTis the type to prettify) because assertions (including matcher expressions) in ScalaTest would then need to look upPrettifiers implicitly by type. This would slow compilation even though most (let's guess 99.9%) of the time in practice assertions do not fail, and thus 99.9% of the time no error messages need to be generated. If no error messages are needed 99.9% of the time, no prettification is needed 99.9% of the time, so the slow down in compile time for the implicit look ups is unlikely to be worth the benefit. Only a few types in practice usually need prettification for testing error message purposes, and those will be covered by the defaultPrettifier. A future version of ScalaTest will provide a simple mechanism to replace the defaultPrettifierwith a custom one when a test actually fails. - trait PrettyMethods extends AnyRef
Provides an implicit conversion that enables
prettyto be invoked on any object, to transform that object into aStringrepresentation. - case class PrettyPair(left: String, right: String, analysis: Option[String]) extends Product with Serializable
- trait Requirements extends AnyRef
Trait that contains
require, andrequireState, andrequireNonNullmethods for checking pre-conditions that give descriptive error messages extracted via a macro.Trait that contains
require, andrequireState, andrequireNonNullmethods for checking pre-conditions that give descriptive error messages extracted via a macro.These methods of trait
Requirementsaim to improve error messages provided when a pre-condition check fails at runtime in production code. Although it is recommended practice to supply helpful error messages when doing pre-condition checks, often people don't. Instead of this:scala> val length = 5 length: Int = 5 scala> val idx = 6 idx: Int = 6 scala> require(idx >= 0 && idx <= length, "index, " + idx + ", was less than zero or greater than or equal to length, " + length) java.lang.IllegalArgumentException: requirement failed: index, 6, was less than zero or greater than or equal to length, 5 at scala.Predef$.require(Predef.scala:233) ...
People write simply:
scala> require(idx >= 0 && idx <= length) java.lang.IllegalArgumentException: requirement failed at scala.Predef$.require(Predef.scala:221) ...
Note that the detail message of the
IllegalArgumentExceptionthrown by the previous line of code is simply,"requirement failed". Such messages often end up in a log file or bug report, where a better error message can save time in debugging the problem. By importing the members ofRequirements(or mixing in its companion trait), you'll get a more helpful error message extracted by a macro, whether or not a clue message is provided:scala> import org.scalactic._ import org.scalactic._ scala> import Requirements._ import Requirements._ scala> require(idx >= 0 && idx <= length) java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5 at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56) ... scala> require(idx >= 0 && idx <= length, "(hopefully that helps)") java.lang.IllegalArgumentException: 6 was greater than or equal to 0, but 6 was not less than or equal to 5 (hopefully that helps) at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56) ...
The
requireStatemethod provides identical error messages torequire, but throwsIllegalStateExceptioninstead ofIllegalArgumentException:scala> val connectionOpen = false connectionOpen: Boolean = false scala> requireState(connectionOpen) java.lang.IllegalStateException: connectionOpen was false at org.scalactic.Requirements$RequirementsHelper.macroRequireState(Requirements.scala:71) ...
Thus, whereas the
requiremethods throw the Java platform's standard exception indicating a passed argument violated a precondition,IllegalArgumentException, therequireStatemethods throw the standard exception indicating an object's method was invoked when the object was in an inappropriate state for that method,IllegalStateException.The
requireNonNullmethod takes one or more variables as arguments and throwsNullArgumentExceptionwith an error messages that includes the variable names if any arenull. Here's an example:scala> val e: String = null e: String = null scala> val f: java.util.Date = null f: java.util.Date = null scala> requireNonNull(a, b, c, d, e, f) org.scalactic.exceptions.NullArgumentException: e and f were null at org.scalactic.Requirements$RequirementsHelper.macroRequireNonNull(Requirements.scala:101) ...
Although trait
Requirementscan help you debug problems that occur in production, bear in mind that a much better alternative is to make it impossible for such events to occur at all. Use the type system to ensure that all pre-conditions are met so that the compiler can find broken pre-conditions and point them out with compiler error messages. When this is not possible or practical, however, traitRequirementsis helpful. - trait SeqEqualityConstraints extends AnyRef
Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaSeqs to one that more closely matches Scala's approach toSeqequality.Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaSeqs to one that more closely matches Scala's approach toSeqequality.Scala's approach to
Seqequality is that if both objects being compared areSeqs, the elements are compared to determine equality. This means you could compare an immutableVectorand a mutableListBufferfor equality, for instance, and get true so long as the twoSeqs contained the same elements in the same order. Here's an example:scala> import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer scala> Vector(1, 2) == ListBuffer(1, 2) res0: Boolean = true
Such a comparison would not, however, compile if you used
===under eitherTypeCheckedTripleEqualsorConversionCheckedTripleEquals, becauseVectorandListBufferare not in a subtype/supertype relationship, nor does an implicit conversion by default exist between them:scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> Vector(1, 2) === ListBuffer(1, 2) <console>:16: error: types scala.collection.immutable.Vector[Int] and scala.collection.mutable.ListBuffer[Int] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.Vector[Int], scala.collection.mutable.ListBuffer[Int]] Vector(1, 2) === ListBuffer(1, 2) ^If you mix or import the implicit conversion provided by
SeqEqualityConstraint, however, the comparison will be allowed:scala> import SeqEqualityConstraints._ import SeqEqualityConstraints._ scala> Vector(1, 2) === ListBuffer(1, 2) res2: Boolean = true
The equality constraint provided by this trait requires that both left and right sides are subclasses of
scala.collection.GenSeqand that anEqualityConstraintcan be found for the element types. In the example above, both theVectorandListBufferare subclasses ofscala.collection.GenSeq, and the regularTypeCheckedTripleEqualsprovides equality constraints for the element types, both of which areInt. By contrast, this trait would not allow aVector[Int]to be compared against aListBuffer[java.util.Date], because no equality constraint will exist between the element typesIntandDate:scala> import java.util.Date import java.util.Date scala> Vector(1, 2) === ListBuffer(new Date, new Date) <console>:20: error: types scala.collection.immutable.Vector[Int] and scala.collection.mutable.ListBuffer[java.util.Date] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.Vector[Int], scala.collection.mutable.ListBuffer[java.util.Date]] Vector(1, 2) === ListBuffer(new Date, new Date) ^ - trait SetEqualityConstraints extends AnyRef
Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaSets to one that more closely matches Scala's approach toSetequality.Provides an implicit method that loosens the equality constraint defined by
TypeCheckedTripleEqualsorConversionCheckedTripleEqualsfor ScalaSets to one that more closely matches Scala's approach toSetequality.Scala's approach to
Setequality is that if both objects being compared areSets, the elements are compared to determine equality. This means you could compare an immutableTreeSetand a mutableHashSetfor equality, for instance, and get true so long as the twoSets contained the same elements in the same order. Here's an example:scala> import scala.collection.immutable.TreeSet import scala.collection.immutable.TreeSet scala> import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet scala> TreeSet(1, 2) == HashSet(1, 2) res0: Boolean = true
Such a comparison would not, however, compile if you used
===under eitherTypeCheckedTripleEqualsorConversionCheckedTripleEquals, becauseTreeSetandHashSetare not in a subtype/supertype relationship, nor does an implicit conversion by default exist between them:scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> TreeSet(1, 2) === HashSet(1, 2) <console>:16: error: types scala.collection.immutable.TreeSet[Int] and scala.collection.mutable.HashSet[Int] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.TreeSet[Int], scala.collection.mutable.HashSet[Int]] TreeSet(1, 2) === HashSet(1, 2) ^If you mix or import the implicit conversion provided by
SetEqualityConstraint, however, the comparison will be allowed:scala> import SetEqualityConstraints._ import SetEqualityConstraints._ scala> TreeSet(1, 2) === HashSet(1, 2) res2: Boolean = true
The equality constraint provided by this trait requires that both left and right sides are subclasses of
scala.collection.GenSetand that anEqualityConstraintcan be found for the element types. In the example above, both theTreeSetandHashSetare subclasses ofscala.collection.GenSet, and the regularTypeCheckedTripleEqualsprovides equality constraints for the element types, both of which areInt. By contrast, this trait would not allow aTreeSet[Int]to be compared against aHashSet[java.util.Date], because no equality constraint will exist between the element typesIntandDate:scala> import java.util.Date import java.util.Date scala> TreeSet(1, 2) === HashSet(new Date, new Date) <console>:20: error: types scala.collection.immutable.TreeSet[Int] and scala.collection.mutable.HashSet[java.util.Date] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[scala.collection.immutable.TreeSet[Int], scala.collection.mutable.HashSet[java.util.Date]] TreeSet(1, 2) === HashSet(new Date, new Date) ^ - final case class Snapshot(name: String, value: Any) extends Product with Serializable
Case class that stores the name and value of a variable or expression.
Case class that stores the name and value of a variable or expression.
See the main documentation for trait
Snapshotsfor more information and examples.- name
the name of the expression
- value
the value of the expression
- final class SnapshotSeq extends IndexedSeq[Snapshot]
An
IndexedSeq[Snapshot]providingtoStringandlinesmethods that can be useful for debug and log messages about program state.An
IndexedSeq[Snapshot]providingtoStringandlinesmethods that can be useful for debug and log messages about program state.See the main documentation for trait
Snapshotsfor more information and examples. - trait Snapshots extends AnyRef
Trait that provides a
snapmethod that takes one or more arguments and results in aSnapshotSeq, whosetoStringlists the names and values of each argument.Trait that provides a
snapmethod that takes one or more arguments and results in aSnapshotSeq, whosetoStringlists the names and values of each argument.The intended use case of this trait is to help you write debug and log messages that give a "snapshot" of program state. Here's an example:
scala> import Snapshots._ import Snapshots._ scala> snap(a, b, c, d, e, f) res3: org.scalactic.SnapshotSeq = a was 1, b was 2, c was 3, d was 4, e was null, f was null
SnapshotSeqoffers alinesmethod that places each variable name/value pair on its own line:scala> snap(a, b, c, d, e, f).lines res4: String = a was 1 b was 2 c was 3 d was 4 e was null f was null
Or, because a
SnapshotSeqis aIndexedSeq[Snapshot], you can process it just like any otherSeq, for example:scala> snap(a, b, c, d, e, f).mkString("Wow! ", ", and ", ". That's so awesome!") res6: String = Wow! a was 1, and b was 2, and c was 3, and d was 4, and e was null, and f was null. That's so awesome! - trait StringNormalizations extends AnyRef
Provides methods that produce
Uniformity[String]instances for various ways to normalize strings for equality comparisons. - trait TimesOnInt extends AnyRef
Trait providing an implicit conversion that adds a
timesmethod toInts that will repeat a given side-effecting operation multiple times.Trait providing an implicit conversion that adds a
timesmethod toInts that will repeat a given side-effecting operation multiple times.Here's an example in which a friendly greeting is printed three times:
3 times println("Hello again, world!")
Running the above code would yield this output:
Hello again, world! Hello again, world! Hello again, world!
If you need to repeat a block of statements multiple times, just enclose them in parentheses, like this:
2 times { print("Hello ") print("again, ") println("world!") }
Running the above code would yield:
Hello again, world! Hello again, world!
This trait enables
timesto be invoked on 0 and any positive integer, but attempting to invoketimeson a negative integer will result in anIllegalArgumentException. - trait Tolerance extends AnyRef
Trait containing an implicit conversion that adds a
+-method toNumerictypes, which enables spreads to be expressed in terms of a pivot and tolerance.Trait containing an implicit conversion that adds a
+-method toNumerictypes, which enables spreads to be expressed in terms of a pivot and tolerance.For example, the
TripleEqualstrait (and its type-checking siblingsTypeCheckedTripleEqualsandConversionCheckedTripleEquals) enable you to write:a === (1.0 +- 0.1)
- trait TolerantNumerics extends AnyRef
Provides
EqualityandEquivalenceinstances forNumerictypes that compare for equality with a given tolerance.Provides
EqualityandEquivalenceinstances forNumerictypes that compare for equality with a given tolerance.Here's an example:
scala> import org.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> 2.001 === 2.0 res0: Boolean = false scala> implicit val doubleEquality = TolerantNumerics.tolerantDoubleEquality(0.01) doubleEquality: org.scalactic.Equality[Double] = org.scalactic.TolerantNumerics$$anon$1@16c2bd13 scala> 2.001 === 2.0 res1: Boolean = true
- trait TraversableEqualityConstraints extends SeqEqualityConstraints with SetEqualityConstraints with MapEqualityConstraints
Provides three implicit methods that loosen the equality constraint defined by
TypeCheckedTripleEqualsfor ScalaTraversables to one that more closely matches Scala's approach toTraversableequality.Provides three implicit methods that loosen the equality constraint defined by
TypeCheckedTripleEqualsfor ScalaTraversables to one that more closely matches Scala's approach toTraversableequality.Scala's approach to
Traversableequality is that if the objects being compared are ether bothSeqs, bothSets, or bothMaps, the elements are compared to determine equality. This means you could compare an immutableVectorand a mutableListBufferfor equality, for instance, and get true so long as the twoSeqs contained the same elements in the same order. Here's an example:scala> import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer scala> Vector(1, 2) == ListBuffer(1, 2) res0: Boolean = true
Such a comparison would not, however, compile if you used
===underTypeCheckedTripleEquals, becauseVectorandListBufferare not in a subtype/supertype relationship:scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> Vector(1, 2) === ListBuffer(1, 2) <console>:16: error: types scala.collection.immutable.Vector[Int] and scala.collection.mutable.ListBuffer[Int] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[scala.collection.immutable.Vector[Int], scala.collection.mutable.ListBuffer[Int]] Vector(1, 2) === ListBuffer(1, 2) ^If you mix or import the implicit conversion provided by
TraversableEqualityConstraint, however, the comparison will be allowed:scala> import TraversableEqualityConstraints._ import TraversableEqualityConstraints._ scala> Vector(1, 2) === ListBuffer(1, 2) res2: Boolean = true
The equality constraints provided by this trait require that left and right sides are both subclasses of either
scala.collection.GenSeq,scala.collection.GenSet, orscala.collection.GenMap, and that anCanEqualcan be found for the element types forSeqandSet, or the key and value types forMaps. In the example above, both theVectorandListBufferare subclasses ofscala.collection.GenSeq, and the regularTypeCheckedTripleEqualsprovides equality constraints for the element types, both of which areInt. By contrast, this trait would not allow aVector[Int]to be compared against aListBuffer[java.util.Date], because no equality constraint will exist between the element typesIntandDate:scala> import java.util.Date import java.util.Date scala> Vector(1, 2) === ListBuffer(new Date, new Date) <console>:20: error: types scala.collection.immutable.Vector[Int] and scala.collection.mutable.ListBuffer[java.util.Date] do not adhere to the equality constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[scala.collection.immutable.Vector[Int], scala.collection.mutable.ListBuffer[java.util.Date]] Vector(1, 2) === ListBuffer(new Date, new Date) ^This trait simply mixes together
SeqEqualityConstraints,SetEqualityConstraints, andMapEqualityConstraints. - trait TripleEquals extends TripleEqualsSupport
Provides
===and!==operators that returnBoolean, delegate the equality determination to anEqualitytype class, and require no relationship between the types of the two values compared.Provides
===and!==operators that returnBoolean, delegate the equality determination to anEqualitytype class, and require no relationship between the types of the two values compared.Recommended Usage: Trait TripleEqualsis useful (in both production and test code) when you need determine equality for a type of object differently than itsequalsmethod: either you can't change theequalsmethod, or theequalsmethod is sensible generally, but you are in a special situation where you need something else. You can use the SuperSafe Community Edition compiler plugin to get a compile-time safety check of types being compared with===. In situations where you need a stricter type check, you can useTypeCheckedTripleEquals.This trait will override or hide implicit methods defined by its sibling trait,
TypeCheckedTripleEquals, and can therefore be used to temporarily turn of type checking in a limited scope. Here's an example, in whichTypeCheckedTripleEqualswill cause a compiler error:import org.scalactic._ import TypeCheckedTripleEquals._
object Example {
def cmp(a: Int, b: Long): Int = { if (a === b) 0 // This line won't compile else if (a < b) -1 else 1 }
def cmp(s: String, t: String): Int = { if (s === t) 0 else if (s < t) -1 else 1 } }Because
IntandLongare not in a subtype/supertype relationship, comparing1and1Lin the context ofTypeCheckedTripleEqualswill generate a compiler error:Example.scala:9: error: types Int and Long do not adhere to the equality constraint selected for the === and !== operators; they must either be in a subtype/supertype relationship; the missing implicit parameter is of type org.scalactic.Constraint[Int,Long] if (a === b) 0 // This line won't compile ^ one error foundYou can “turn off” the type checking locally by importing the members of
TripleEqualsin a limited scope:package org.scalactic.examples.tripleequals
import org.scalactic._ import TypeCheckedTripleEquals._
object Example {
def cmp(a: Int, b: Long): Int = { import TripleEquals._ if (a === b) 0 else if (a < b) -1 else 1 }
def cmp(s: String, t: String): Int = { if (s === t) 0 else if (s < t) -1 else 1 } }With the above change, the
Example.scalafile compiles fine. Type checking is turned off only inside the firstcmpmethod that takes anIntand aLong.TypeCheckedTripleEqualsis still enforcing its type constraint, for example, for thes === texpression in the other overloadedcmpmethod that takes strings.Because the methods in
TripleEquals(and its siblings)override all the methods defined in supertypeTripleEqualsSupport, you can achieve the same kind of nested tuning of equality constraints whether you mix in traits, import from companion objects, or use some combination of both.In short, you should be able to select a primary constraint level via either a mixin or import, then change that in nested scopes however you want, again either through a mixin or import, without getting any implicit conversion ambiguity. The innermost constraint level in scope will always be in force.
- trait TripleEqualsSupport extends AnyRef
Trait that defines abstract methods used to enforce compile-time type constraints for equality comparisons, and defines
===and!==operators used by matchers.Trait that defines abstract methods used to enforce compile-time type constraints for equality comparisons, and defines
===and!==operators used by matchers.The abstract methods of this trait are selectively implemented as implicit by subclasses to enable a spectrum of type constraints for the
===and!==operators. As an illustration, if in the expression,a === b, the type ofaisAandbisB, the following three levels of compile-time checking can be obtained fromTripleEqualsSupportsubtraits:Unchecked -
AandBcan be any two types. This constraint level is available from subtraitTripleEquals.Statically-checked -
Amust be a subtype ofB, or vice versa, or must cooperate such that the equality laws stated in theequalscontract are preserved. This (intermediate) constraint level is available by using subtraitTripleEqualsand installing the SuperSafe Community Edition Scala compiler plugin.Type-checked -
Amust be a subtype ofB, or vice versa. (BothAandBcan be the same type, because a type is considered a subtype of itself.) This (strongest) constraint level is available from subtraitTypeCheckedTripleEquals.This trait defines all methods that need to be defined implicitly by the subtraits so that if multiple subtraits are used together, the inner-most subtrait in scope can not only enable the implicits it needs by overriding or hiding those methods (currently-in-scope as regular, non-implicit methods) and making them implicit, it can also disable any implicits enabled by its sibling subtraits in enclosing scopes. For example, if your test class mixes in
TypeCheckedTripleEquals, inside your test class the following methods will be implicit:convertToCheckingEqualizertypeCheckedConstraintlowPriorityTypeCheckedConstraintconvertEquivalenceToAToBConstraintconvertEquivalenceToBToAConstraint
If in the body of a test you want to turn off the type checking, you can import the members of
TripleEqualsin the body of that test. This will not only hide non-implicit methodsconvertToEqualizerunconstrainedEqualityofTypeCheckedTripleEquals, replacing those with implicit ones defined inTripleEquals, it will also hide the three methods made implicit inTypeCheckedTripleEquals(and listed above), replacing them by non-implicit ones.In short, you should be able to select a primary constraint level via either a mixin or import, then change that in nested scopes however you want, again either through a mixin or import, without getting any implicit conversion ambiguity. The innermost constraint level in scope will always be in force.
- trait TrySugar extends AnyRef
Trait providing an implicit class that adds a
toOrmethod toTry, which convertsSuccesstoGood, andFailuretoBad, as well as avalidatingmethod, which takes one or more validation functions and returns either the sameTryif either theTryhad already failed or its value passes all the functions, orValidationFailedExceptioncontaining an error message describing the first validation that failed.Trait providing an implicit class that adds a
toOrmethod toTry, which convertsSuccesstoGood, andFailuretoBad, as well as avalidatingmethod, which takes one or more validation functions and returns either the sameTryif either theTryhad already failed or its value passes all the functions, orValidationFailedExceptioncontaining an error message describing the first validation that failed.Here's an example validation method, which passes if the given
Intis evenly divisible by 10 (i.e., the result will bePass). If the value does not pass this test, the result is aFailcontaining a helpful error message string.scala> import org.scalactic._ import org.scalactic._ scala> import TrySugar._ import TrySugar._ scala> import scala.util.Try import scala.util.Try scala> def isRound(i: Int): Validation[ErrorMessage] = | if (i % 10 == 0) Pass else Fail(i + " was not a round number") isRound: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage]Validation will be attempted on a successful
Try. If the validation succeeds, the resultingTrywill be the same successfulTrywith the same value. (A "validation" only transforms theTryif the validation fails, otherwise it is the sameTry. The only difference is its value has now been proven valid.) In the following example, a successfulTry[Int]with the value 100 passes the validation (which checks whether 100 is evenly divisible by 10), therefore the result of thevalidatingcall is the same successfulTrywith the same value.scala> val try100 = Try(100) try100: scala.util.Try[Int] = Success(100) scala> val round100 = try100.validating(isRound) round100: scala.util.Try[Int] = Success(100)
If validation fails, the successful
Trywill be transformed into a failed one, with aValidationFailedExceptionthat contains the error message returned by the validation function. In the following example, 42 fails the validation because it is not evenly divisible by 10:scala> val try42 = Try(42) try42: scala.util.Try[Int] = Success(42) scala> val round42 = try42.validating(isRound) round42: scala.util.Try[Int] = Failure(org.scalactic.exceptions.ValidationFailedException: 42 was not a round number)
If
validatingis called on a failedTry, it just returns the same failedTry:scala> val tryEx = Try[Int] { throw new Exception("oops!") } tryEx: scala.util.Try[Int] = Failure(java.lang.Exception: oops!) scala> val roundEx = tryEx.validating(isRound) roundEx: scala.util.Try[Int] = Failure(java.lang.Exception: oops!)The
validatingmethod accepts one or more validation functions. If you pass more than one, they will be tried in order up until the first failure, whose error message will appear in theValidationFailedException. In other words,validatingwill short circuit at the first error and return that. It will not accumulate errors. For example, the following validation will short circuit after theisDivBy3function fails:scala> def isDivBy3(i: Int): Validation[ErrorMessage] = | if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3") isDivBy3: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage] scala> def isAnswerToLifeTheUniverseAndEverything(i: Int): Validation[ErrorMessage] = | if (i == 42) Pass else Fail(i + " did not equal 42") isAnswerToLifeTheUniverseAndEverything: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage] scala> try100.validating(isRound, isDivBy3, isAnswerToLifeTheUniverseAndEverything) res0: scala.util.Try[Int] = Failure(org.scalactic.exceptions.ValidationFailedException: 100 was not divisible by 3)Here are some examples of the
toOrmethod:scala> try100.toOr res1: org.scalactic.Or[Int,Throwable] = Good(100) scala> tryEx.toOr res2: org.scalactic.Or[Int,Throwable] = Bad(java.lang.Exception: oops!)
- trait TypeCheckedTripleEquals extends LowPriorityTypeCheckedConstraint
Provides
===and!==operators that returnBoolean, delegate the equality determination to anEqualitytype class, and require the types of the two values compared to be in a subtype/supertype relationship.Provides
===and!==operators that returnBoolean, delegate the equality determination to anEqualitytype class, and require the types of the two values compared to be in a subtype/supertype relationship.Recommended Usage: Trait TypeCheckedTripleEqualsis useful (in both production and test code) when you need a stricter type check than is provided by the SuperSafe Community Edition compiler plugin forTripleEquals. For example, if you are developing a library that uses advanced features of Scala's type system, you may want to enforce in your tests that the types appearing in equality comparisons match exactly.By default under
TripleEquals, any use of===will compile, just like the==operator:scala> import org.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> 1L === 1 // A Long can equal an Int res0: Boolean = true scala> List(1, 2, 3) === Vector(1, 2, 3) // A List can equal a Vector res1: Boolean = true scala> "hi" === 1 // Likely a bug, because a String can never equal an Int res2: Boolean = false
With SuperSafe Community Edition installed, the first two expressions above will be allowed to compile, but the third (which represents a likely bug) will not:
scala> import org.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> 1L === 1 res0: Boolean = true scala> List(1, 2, 3) === Vector(1, 2, 3) res1: Boolean = true scala> "hi" === 1 // SuperSafe catches the bug at compile time <console>:17: error: [Artima SuperSafe] Values of type String and Int may not be compared with the === operator. If you really want to compare them for equality, configure Artima SuperSafe to allow those types to be compared for equality. For more information on this kind of error, see: http://www.artima.com/supersafe_user_guide.html#safer-equality "hi" === 1 ^By contrast,
TypeCheckedTripleEqualswill prevent any of the above three expressions from compiling:scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> 1L === 1 <console>:17: error: types Long and Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Long,Int] 1L === 1 ^ scala> List(1, 2, 3) === Vector(1, 2, 3) <console>:17: error: types List[Int] and scala.collection.immutable.Vector[Int] do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[List[Int],scala.collection.immutable.Vector[Int]] List(1, 2, 3) === Vector(1, 2, 3) ^ scala> "hi" === 1 <console>:17: error: types String and Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[String,Int] "hi" === 1 ^Trait
TypeCheckedTripleEqualsrejects comparisons of typesIntandLong,List[Int]andVector[Int], andStringandInt, because none have a direct subtype/supertype relationship. To compare two types that are unrelated by inheritance underTypeCheckedTripleEquals, you could convert one of them to the other type (because a type is both a subtype and supertype of itself). Here's an example:scala> 1L === 1.toLong // Now both sides are Long res0: Boolean = true scala> List(1, 2, 3) === Vector(1, 2, 3).toList // Now both sides are List[Int] res1: Boolean = true
Nevertheless, a better (and the recommended) way to deal with this situation is to use a widening type ascription. A type ascription is simply a colon and a type placed next to a variable, usually surrounded by parentheses. For example, because
AnyValis a common supertype ofIntandLong, you could solve the type error by widening the type of one side or the other toAnyVal. BecauseAnyValis a supertype of bothIntandLong, the type constraint will be satisfied:scala> 1 === (1L: AnyVal) res2: Boolean = true scala> (1: AnyVal) === 1L res3: Boolean = true
Similarly, since
Seq[Int]is a common supertype of bothVector[Int]andList[Int], the type constraint can be satisfied by widening either to their common supertype,Seq[Int]:scala> List(1, 2, 3) === (Vector(1, 2, 3): Seq[Int]) res4: Boolean = true scala> (List(1, 2, 3): Seq[Int]) === Vector(1, 2, 3) res5: Boolean = true
The primary intended use case for
TypeCheckedTripleEqualsis to enable tests to be very strict about which types can compared for equality, but it can also be used with production code where this level of strictness is desired.Forcing implicit conversions before equality checks
You can also use a type ascription to force an implicit conversion before a value participates in an equality comparison. For example, although
JavaConversionsprovides an implicit conversion betweenjava.util.Setandscala.collection.mutable.Set, underTypeCheckedTripleEqualsan equality comparison between those two types will not be allowed:scala> import collection.JavaConversions._ import collection.JavaConversions._ scala> import collection.mutable import collection.mutable scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> mutable.Set.empty[String] === new java.util.HashSet[String] <console>:36: error: types scala.collection.mutable.Set[String] and java.util.HashSet[String] do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[scala.collection.mutable.Set[String],java.util.HashSet[String]] mutable.Set.empty[String] === new java.util.HashSet[String] ^To force an implicit conversion of the Java
HashSetto a Scalamutable.Set, after which the type constraint will be satisfied, you can use a type ascription:scala> mutable.Set.empty[String] === (new java.util.HashSet[String]: mutable.Set[String]) res0: Boolean = true
Scoping equality policies
This trait will override or hide implicit methods defined by
TripleEqualsand can therefore be used to temporarily turn on or off type checking in a limited scope. Here's an example, in whichTypeCheckedTripleEqualswill cause a compiler error:import org.scalactic._ import TypeCheckedTripleEquals._
object Example {
def cmp(a: Int, b: Long): Int = { if (a === b) 0 // This line won't compile else if (a < b) -1 else 1 }
def cmp(s: String, t: String): Int = { if (s === t) 0 else if (s < t) -1 else 1 } }Because
IntandLongare not in a subtype/supertype relationship, comparing1and1Lin the context ofTypeCheckedTripleEqualswill generate a compiler error:Example.scala:9: error: types Int and Long do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Int,Long] if (a === b) 0 // This line won't compile ^ one error foundYou can “relax” the type checking locally by importing the members of
TripleEqualsin a limited scope:package org.scalactic.examples.conversioncheckedtripleequals
import org.scalactic._ import TypeCheckedTripleEquals._
object Example {
def cmp(a: Int, b: Long): Int = { import TripleEquals._ if (a === b) 0 else if (a < b) -1 else 1 }
def cmp(s: String, t: String): Int = { if (s === t) 0 else if (s < t) -1 else 1 } }With the above change, the
Example.scalafile compiles fine. The strict checking is disabled only inside the firstcmpmethod that takes anIntand aLong.TypeCheckedTripleEqualsis still enforcing its type constraint, for example, for thes === texpression in the other overloadedcmpmethod that takes strings.Because the methods
TripleEqualsandTypeCheckedTripleEqualsoverride all the methods defined in supertypeTripleEqualsSupport, you can achieve the same kind of nested tuning of equality constraints whether you mix in traits, import from companion objects, or use some combination of both.In short, you should be able to select a primary constraint level via either a mixin or import, then change that in nested scopes however you want, again either through a mixin or import, without getting any implicit conversion ambiguity. The innermost constraint level in scope will always be in force.
- trait Uniformity[A] extends Normalization[A]
Defines a custom way to normalize instances of a type that can also handle normalization of that type when passed as
Any.Defines a custom way to normalize instances of a type that can also handle normalization of that type when passed as
Any.For example, to normalize
Doubles by truncating off any decimal part, you might write:import org.scalactic._
val truncated = new Uniformity[Double] { def normalized(d: Double) = d.floor def normalizedCanHandle(o: Any) = o.isInstanceOf[Double] def normalizedOrSame(o: Any): Any = o match { case d: Double => normalized(d) case _ => o } }Given this definition you could use it with the
ExplicitlyDSL like this:import org.scalatest._ import Matchers._
2.1 should equal (2.0) (after being truncated)If you make the
truncatedvalimplicit and import or mix in the members ofNormMethods, you can access the behavior by invoking.normonDoubles.implicit val doubleUniformity = truncated import NormMethods._
val d = 2.1 d.norm // returns 2.0Note that by creating a
Uniformityrather than just an instance of its supertype,Normalization, it can be used more generally. For example,Uniformitys allow you to theExplicitlyDSL withTripleEquals, whereasNormalizations requireTypeCheckedTripleEquals.Uniformitys also enable you to use theExplicitlyDSL with ScalaTest'sshould===,equal, andcontainmatcher syntax, whereas a plainNormalizationcan only be used withshould===, and only underTypeCheckedTripleEquals.- A
the type whose uniformity is being defined
- sealed trait Validation[+E] extends Product with Serializable
Represents the result of a validation, either the object
Passif the validation succeeded, else an instance ofFailcontaining an error value describing the validation failure.Represents the result of a validation, either the object
Passif the validation succeeded, else an instance ofFailcontaining an error value describing the validation failure.Validations are used to filterOrs inforexpressions orfiltermethod calls. For example, consider these methods:import org.scalactic._
def isRound(i: Int): Validation[ErrorMessage] = if (i % 10 == 0) Pass else Fail(i + " was not a round number")
def isDivBy3(i: Int): Validation[ErrorMessage] = if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3")Because
isRoundandisDivBy3take anIntand return aValidation[ErrorMessage], you can use them in filters inforexpressions involvingOrs of typeIntOrErrorMessage. Here's an example:for (i <- Good(3) if isRound(i) && isDivBy3(i)) yield i // Result: Bad(3 was not a round number)
Validations can also be used to accumulate error usingwhen, a method that's made available by traitAccumulationon accumualtingOrs (Ors whoseBadtype is anEvery[T]). Here are some examples:import Accumulation._
for (i <- Good(3) when (isRound, isDivBy3)) yield i // Result: Bad(One(3 was not a round number))
for (i <- Good(4) when (isRound, isDivBy3)) yield i // Result: Bad(Many(4 was not a round number, 4 was not divisible by 3))Note: You can think of
Validationas an “Optionwith attitude,” wherePassis aNonethat indicates validation success andFailis aSomewhose value describes the validation failure.- E
the type of error value describing a validation failure for this
Validation
Deprecated Type Members
- type Chain[+T] = NonEmptyList[T]
The name
org.scalactic.Chainhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.NonEmptyList, instead.The name
org.scalactic.Chainhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.NonEmptyList, instead.This type has been renamed for consistency with other '
NonEmpty' anyvals.- Annotations
- @deprecated
- Deprecated
(Since version 3.1.0) Chain has been deprecated and will be removed in a future version of Scalactic. Please use org.scalactic.anyvals.NonEmptyList instead.
Value Members
- val ScalacticVersion: String
The version number of Scalactic.
The version number of Scalactic.
- returns
the Scalactic version number.
- def attempt[R](f: => R): Or[R, Throwable]
Returns the result of evaluating the given block
f, wrapped in aGood, or if an exception is thrown, theThrowable, wrapped in aBad.Returns the result of evaluating the given block
f, wrapped in aGood, or if an exception is thrown, theThrowable, wrapped in aBad.Here are some examples:
scala> import org.scalactic._ import org.scalactic._ scala> attempt { 2 / 1 } res0: org.scalactic.Or[Int,Throwable] = Good(2) scala> attempt { 2 / 0 } res1: org.scalactic.Or[Int,Throwable] = Bad(java.lang.ArithmeticException: / by zero)- f
the block to attempt to evaluate
- returns
the result of evaluating the block, wrapped in a
Good, or the thrown exception, wrapped in aBad
- object Accumulation extends Accumulation
Companion object to trait
Accumulationthat allowsAccumulation's members to be imported rather than mixed in, and also contains nested traits used by implicit conversions declared in traitAccumulations.Companion object to trait
Accumulationthat allowsAccumulation's members to be imported rather than mixed in, and also contains nested traits used by implicit conversions declared in traitAccumulations.For more information and examples, see the Accumulating errors with
Orsection of the main documentation for classOr. - object Bool
Boolcompanion object that provides factory methods to create different sub types ofBoolBoolcompanion object that provides factory methods to create different sub types ofBoolBoolis used by code generated fromBooleanMacro(whichAssertionsMacroandRequirementsMacrouses), it needs to be public so that the generated code can be compiled. It is expected that ScalaTest users would ever need to useBooldirectly. - object Catcher
Companion object for
Catcherthat provides a factory method for creatingThrowableextractors. - object DefaultEquality
- object Equality
Companion object for trait
Equalitythat provides factory methods for producingEqualityinstances. - object Equivalence
Companion object for trait
Equivalencethat provides a factory method for producing defaultEquivalenceinstances. - object Every extends Serializable
Companion object for abstract class
Every. - object Explicitly extends Explicitly
Companion object for
Explicitly, which enables the Scalactic explicitly DSL to be imported rather than mixed in, like this:Companion object for
Explicitly, which enables the Scalactic explicitly DSL to be imported rather than mixed in, like this:import org.scalactic._ import Explicitly._
// Use the explicitly DSL... - object FutureSugar extends FutureSugar
Companion object for
FutureSugarenabling its members to be imported as an alternative to mixing them in. - object Good extends Serializable
Companion object for
Goodthat offers, in addition to the standard factory method forGoodthat takes single “good” type, an parameterless apply used to narrow theGoodtype when creating aBad.Companion object for
Goodthat offers, in addition to the standard factory method forGoodthat takes single “good” type, an parameterless apply used to narrow theGoodtype when creating aBad. - object MapEqualityConstraints extends MapEqualityConstraints
Companion object that facilitates the importing of
MapEqualityConstraintsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
MapEqualityConstraintsmembers as an alternative to mixing it in. One use case is to importMapEqualityConstraintsmembers so you can use them in the Scala interpreter. - object NormMethods extends NormMethods
Companion object for
NormMethodsenabling its members to be imported as an alternative to mixing them in. - object OptionSugar extends OptionSugar
Companion object for
OptionSugarenabling its members to be imported as an alternative to mixing them in. - object Or extends Serializable
The companion object for
Orproviding factory methods for creatingOrs fromEithers andTrys. - object Pass extends Validation[Nothing] with Product with Serializable
Indicates a validation succeeded.
- object Prettifier extends Serializable
Companion object for
Prettifierthat provides a defaultPrettifierimplementation. - object PrettyMethods extends PrettyMethods
Companion object for trait
PrettyMethodsenabling its members to be imported as an alternative to mixing them in. - object Requirements extends Requirements
Companion object that facilitates the importing of
Requirementsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
Requirementsmembers as an alternative to mixing it in. One use case is to importRequirementsmembers so you can use them in the Scala interpreter:$scala -classpath scalatest.jar Welcome to Scala version 2.10.3.final (Java HotSpot(TM) Client VM, Java xxxxxx). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalactic.Requirements._ import org.scalactic.Requirements._ scala> val a = 1 a: Int = 1 scala> require(a == 2) java.lang.IllegalArgumentException: 1 did not equal 2 at org.scalactic.Requirements$RequirementsHelper.macroRequire(Requirements.scala:56) at .<init>(<console>:20) at .<clinit>(<console>) at .<init>(<console>:7) at .<clinit>(<console>) at $print(<console>) at sun.reflect.NativeMethodAccessorImpl.invoke... - object SeqEqualityConstraints extends SeqEqualityConstraints
Companion object that facilitates the importing of
SeqEqualityConstraintsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
SeqEqualityConstraintsmembers as an alternative to mixing it in. One use case is to importSeqEqualityConstraintsmembers so you can use them in the Scala interpreter. - object SetEqualityConstraints extends SetEqualityConstraints
Companion object that facilitates the importing of
SetEqualityConstraintsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
SetEqualityConstraintsmembers as an alternative to mixing it in. One use case is to importSetEqualityConstraintsmembers so you can use them in the Scala interpreter. - object SnapshotSeq
- object Snapshots extends Snapshots
Companion object that facilitates the importing of
Snapshotsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
Snapshotsmembers as an alternative to mixing it in. One use case is to importSnapshotsmembers so you can use them in the Scala interpreter:$scala -classpath scalatest.jar Welcome to Scala version 2.10.3.final (Java HotSpot(TM) Client VM, Java xxxxxx). Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalactic.Snapshots._ import org.scalatest.Snapshots._ scala> val a = 8 a: Int = 8 scala> snap(a) res0: scala.collection.immutable.Vector[org.scalactic.Snapshot] = Vector(a = 8)
- object StringNormalizations extends StringNormalizations
Companion object to trait
StringNormalizationsthat provides an alternative to mixing it in. - object TimesOnInt extends TimesOnInt
Companion object that facilitates the importing of
TimesOnIntmembers as an alternative to mixing it in.Companion object that facilitates the importing of
TimesOnIntmembers as an alternative to mixing it in.One use case of this companion object is to import
TimesOnIntmembers so you can use them in the Scala interpreter. Here's an example:scala> import org.scalatest.TimesOnInt._ import org.scalatest.TimesOnInt._ scala> 3 times println("Hello again, world!") Hello again, world! Hello again, world! Hello again, world! - object Tolerance extends Tolerance
Companion object to trait
Tolerancethat facilitates the importing ofTolerancemembers as an alternative to mixing it in.Companion object to trait
Tolerancethat facilitates the importing ofTolerancemembers as an alternative to mixing it in. One use case is to importTolerancemembers so you can use them in the Scala interpreter:$ scala -classpath scalactic.jar Welcome to Scala version 2.10.0 Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalactic._ import org.scalactic._ scala> import Tolerance._ import Tolerance._ scala> 1.0 +- 0.1 res0: org.scalactic.TripleEqualsSupport.Spread[Double] = Spread(1.0,0.1)
- object TolerantNumerics extends TolerantNumerics
Companion object for
TolerantNumericsthat enables its members to be imported as an alternative to mixing them in. - object TraversableEqualityConstraints extends TraversableEqualityConstraints
Companion object that facilitates the importing of
TraversableEqualityConstraintsmembers as an alternative to mixing it in.Companion object that facilitates the importing of
TraversableEqualityConstraintsmembers as an alternative to mixing it in. One use case is to importTraversableEqualityConstraintsmembers so you can use them in the Scala interpreter. - object TripleEquals extends TripleEquals
Companion object to trait
TripleEqualsthat facilitates the importing ofTripleEqualsmembers as an alternative to mixing it in.Companion object to trait
TripleEqualsthat facilitates the importing ofTripleEqualsmembers as an alternative to mixing it in. One use case is to importTripleEqualsmembers so you can use them in the Scala interpreter:$ scala -classpath scalatest.jar Welcome to Scala version 2.10.0 Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalactic._ import org.scalactic._ scala> import TripleEquals._ import TripleEquals._ scala> 1 + 1 === 2 res0: Boolean = true
- object TripleEqualsSupport
- object TrySugar extends TrySugar
Companion object for
TrySugarenabling its members to be imported as an alternative to mixing them in. - object TypeCheckedTripleEquals extends TypeCheckedTripleEquals
Companion object to trait
TypeCheckedTripleEqualsthat facilitates the importing ofTypeCheckedTripleEqualsmembers as an alternative to mixing it in.Companion object to trait
TypeCheckedTripleEqualsthat facilitates the importing ofTypeCheckedTripleEqualsmembers as an alternative to mixing it in. One use case is to importTypeCheckedTripleEqualsmembers so you can use them in the Scala interpreter:$ scala -classpath scalatest.jar Welcome to Scala version 2.10.0 Type in expressions to have them evaluated. Type :help for more information. scala> import org.scalactic._ import org.scalactic._ scala> import TypeCheckedTripleEquals._ import TypeCheckedTripleEquals._ scala> 1 + 1 === 2 res0: Boolean = true
Deprecated Value Members
- val Chain: NonEmptyList.type
The name
org.scalactic.Chainhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.NonEmptyList, instead.The name
org.scalactic.Chainhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.NonEmptyList, instead.This type has been renamed for consistency with other '
NonEmpty' anyvals.- Annotations
- @deprecated
- Deprecated
(Since version 3.1.0) Chain has been deprecated and will be removed in a future version of Scalactic. Please use org.scalactic.anyvals.NonEmptyList instead.
- val End: scalactic.anyvals.End.type
The name
org.scalactic.Endhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.End, instead.The name
org.scalactic.Endhas been deprecated and will be removed in a future version of Scalactic. Please use its new name,org.scalatest.anyvals.End, instead.This type has been renamed for consistency with other '
NonEmpty' anyvals.- Annotations
- @deprecated
- Deprecated
(Since version 3.1.0) End has been deprecated and will be removed in a future version of Scalactic. Please use org.scalactic.anyvals.End instead.