Skip to content

Instantly share code, notes, and snippets.

@mpilquist
Last active December 31, 2015 16:29
Show Gist options
  • Select an option

  • Save mpilquist/8014394 to your computer and use it in GitHub Desktop.

Select an option

Save mpilquist/8014394 to your computer and use it in GitHub Desktop.
scala> import org.scalatest._
scala> import Matchers._
scala> import Inspectors._
scala> trap { List('a', 'b') should have length 1 }
res0: Throwable = org.scalatest.exceptions.TestFailedException: List(a, b) had length 2 instead of expected length 1
scala> trap { List(1, 2, 3) should contain (4) }
res1: Throwable = org.scalatest.exceptions.TestFailedException: List(1, 2, 3) did not contain element 4
scala> trap { forEvery (List(1, 2, 3)) { x => x should be < 2 } }
res2: Throwable =
org.scalatest.exceptions.TestFailedException: forEvery failed, because:
at index 1, 2 was not less than 2 (<console>:21),
at index 2, 3 was not less than 2 (<console>:21)
in List(1, 2, 3)
scala> import org.scalautils.StringNormalizations._
scala> trap { (Array("Doe", "Me") should contain oneOf ("X", "RAY", "BEAM")) (after being lowerCased) }
res3: Throwable = org.scalatest.exceptions.TestFailedException: Array(Doe, Me) did not contain one of ("X", "RAY", "BEAM")
@bvenners
Copy link

Hi Runar,

Perhaps proving your point, I awoke this morning realizing I had grabbed the wrong overloaded being method in my previous comment. Normally the way to figure out which method is called is to follow the types. The type of lowerCased is Uniformity, which is a subtype of Normalization that can also do "universal normalization", i.e., normalize an Any. This was needed because I felt the need to support universal equality via Equality for compatibility reasons. I want ScalaTest to be easily used to test anything that can be thrown at it on the Java platform, including any kind of object written in Java or any other JVM language.

When it came time to add tunable type checks, I at first resisted having two different traits, Equality and Equivalence, and just had Equality. But that meant users would always need to handle Any both for alternate equality implementations and normalizations. That's often a pain, and after our conversation at the Mexican restaurant, and more anguished thinking about it, I decided to just have two traits each. Equality/Uniformity handle Any on the right hand side. Equivalence/Normalization can be used when both left and right are the same type, which === enables. And symmetrically, Equality extends Equivalence; Uniformity extends Normalization. The universal trait is in both cases a subtype of the more restrictive trait.

So the actual being method being called in the (after being lowerCased) case is the other overloaded being method in trait Explicitly, which looks like:

def being[N](uniformity: Uniformity[N])(implicit equality: Equality[N]): NormalizingEquality[N]

Essentially what the two overloaded being methods do is transform a normalization typeclass (either Uniformity or Normalization) into an equality typeclass (either NormalizingEquality or a NormalizingEquivalence) that first normalizes both the left and right sides, and then delegates to the implicitly grabbed equality typeclass (either Equality or Equivalence) to compare the normalized forms.

Bill

@SethTisue
Copy link

we've been taking the approach of using macros ... The plan is, in the next release actually, to give good error messages for Boolean expressions that equate to the built-in matchers

That sounds terrific! Looking forward to it.

(I currently never go near the matchers stuff, because what Paul and Runar said.)

@bvenners
Copy link

Hi Paul,

By the way, in 2.1.0-RC3 I added toEquality and toEquivalance methods to Uniformity and Normalization, so that you need not use the English-like syntax to go, for example, from a Uniformity to an Equality. Instead of:

(result === "hello")(after being lowerCased)

You can just write:

(result === "hello")(lowerCased.toEquality)

I got the memo from multiple sources that even some of the folks who like seeing English-like DSLs in test code don't want to see it in the production code.

Bill

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment