Latest Release - Scalactic 3.2.0!

The Scalactic library is focused on constructs related to quality that are useful in both production code and tests. Although ScalaTest is tightly integrated with Scalactic, you can use Scalactic with any Scala project and any test framework. The Scalactic library has no dependencies other than Scala itself.

The === and !== operators

Scalactic provides a powerful === operator (and its complement, !== ) that allows you to

Customize equality for a type outside its class

Get compiler errors for suspicious equality comparisons

Compare numeric values for equality with a tolerance

Normalize values before comparing them for equality

For example, you can compare strings for equality after being normalized by forcing them to lowercase by customizing Equality explicitly, like this:

import org.scalactic._ import TripleEquals._ import StringNormalizations._ import Explicitly._ ( "Hello" === "hello" ) (after being lowerCased)

You can also define implicit Normalization strategies for types and access them by invoking norm methods:

import NormMethods._ implicit val strNormalization = lowerCased "WHISPER" .norm "WHISPER" .norm === "whisper"

The “ after being lowerCased ” syntax shown previously is provided by Scalactic' Explicitly DSL, which allows you to specify Equality explicitly. You can also define custom Equality s implicitly:

implicit val strEquality = decided by defaultEquality[ String ] afterBeing lowerCased "Hello" === "hello" "normalized" === "NORMALIZED"

You can compare numeric values for equality with a Tolerance , like this:

import Tolerance._ 2.00001 === 2.0 +- 0.01

Or you could use TolerantNumerics define an implicit Equality[Double] that compares Double s with a tolerance:

import TolerantNumerics._ implicit val dblEquality = tolerantDoubleEquality( 0.01 ) 2.00001 === 2.0

A compiler error for an equality comparison that would always yield false looks like:

import TypeCheckedTripleEquals._ Some ( "hi" ) === "hi" error: types Some[String] and String do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.Constraint[Some[String],String] Some("hi") === "hi" ^

Or and Every

Scalactic provides an “ Either with attitude” named Or , designed for functional error handling. Or gives you more convenient chaining of map and flatMap calls (and for expressions) than Either and, when, combined with Every , enables you to accumulate errors. Every is an ordered collection of one or more elements. An Or is either Good or Bad . An Every is either One or Many . Here's an example of accumulating errors with Or and Every :

import org.scalactic._ import Accumulation._ case class Person (name: String , age: Int ) 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""" )) } 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""" )) } } def parsePerson(inputName: String , inputAge: String ): Person Or Every[ErrorMessage] = { val name = parseName(inputName) val age = parseAge(inputAge) withGood(name, age) { Person (_, _) } }

Here are some examples of parsePerson in action:

parsePerson( "Bridget Jones" , "29" )

parsePerson( "Bridget Jones" , "" )

parsePerson( "Bridget Jones" , "-29" )

parsePerson( "" , "" )

Or offers several other ways to accumulate errors besides the withGood methods shown in the example above. See the documentation for Or for more information.

Requirements and Snapshots

Scalactic includes a Requirements trait that offers require , requireState , and requireNonNull methods for checking pre-conditions that give descriptive error messages extracted via a macro. Here are some examples:

val a = -1 require(a >= 0 ) requireState(a >= 0 ) val b: String = null requireNonNull(a, b)

Trait Snapshots offers a snap method that can help you make debug and log messages that include information about the values of variables:

val a = 1 val b = '2' val c = "3" snap(a, b, c)

And more (but not much more)...

Scalactic also includes a TimesOnInt trait that allows you to perform side-effecting loops a specified number of times, like this:

import TimesOnInt._ 3 times println( "hello " )

You can also define an alternate String forms for types using Prettifier s and create extractors for Throwable s via the Catcher factory.

And that's it: Scalactic is a small, very focused library. Why not give it a try? Just visit the Quick Start page.