Implement sophisticated custom matchers

168 views
Skip to first unread message

Muki

unread,
Jan 28, 2012, 7:57:26 AM1/28/12
to scalatest-users
Hi,

I'm trying to implement some custom matchers (should, have, be and
custom words)
for my framework. My goals are defined here:
https://github.com/knowing/Knowing/wiki/Test-Framework-(Scala)

I got two problems.

1. Implicit conversion of java.util.List
I have a class weka.core.Instances which inherits from
java.util.AbstractList.
This problem isn't so hard, because I use this class always wrapped.
however
it would be nice if I can match on this class, too.

2. Sealed classes and package scopes.
I create a trait extending _ShouldMatchers_ with a body seen here
https://gist.github.com/1694204

However I cannot extend _ResultOfNotWordForAnyRef_ as it's a sealed
class
and I'm not able to use some methods defined in org.scalatest. It
would be really
awesome if this would be possible. Or if I could contribute my work in
someway
so this matching works.

thx in advance,
Muki

Bill Venners

unread,
Feb 8, 2012, 5:35:47 PM2/8/12
to scalate...@googlegroups.com
HI Muki,

Sorry for the delay in responding to this. I for some reason didn't
get notified by Google Groups that it was pending. I only found it
yesterday. The link to your goals isn't working. Can you send another
link?

Thanks.

Bill

> --
> You received this message because you are subscribed to the Google
> Groups "scalatest-users" group.
> To post to this group, send email to scalate...@googlegroups.com
> To unsubscribe from this group, send email to
> scalatest-use...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/scalatest-users?hl=en
> ScalaTest itself, and documentation, is available here:
> http://www.artima.com/scalatest

--
Bill Venners
Artima, Inc.
http://www.artima.com

Muki

unread,
Feb 9, 2012, 3:26:53 PM2/9/12
to scalatest-users
Hi Bill,

Nevermind :)
Sorry for the missleading link. I'd changed the wiki structure in the
meantime. The correct link is:
https://github.com/knowing/Knowing/wiki/1.4-Test-Framework-(Scala)

thanks in advance,
Muki

edmondo1984

unread,
Feb 11, 2012, 4:18:17 AM2/11/12
to scalatest-users
I have kind of a similar problem, I would like to write a Matcher that
can compare two array of doubles with a given tolerance.

However, the guide and the scaladoc describes how to write "one side
matcher"... like how to check if a file is empty, if it exists... but
does not explain how to write a matcher that checks if a file has the
same length of another file, for example...

Thank you for your help
Best Regards

Bill Venners

unread,
Feb 11, 2012, 6:43:57 AM2/11/12
to scalate...@googlegroups.com
Hi Edmondo,

Can you submit a couple examples of how you'd like the resulting code
to look? In other words, once your custom matcher is done, how would
someone use it?

Thanks.

Bill

Edmondo Porcu

unread,
Feb 11, 2012, 6:48:23 AM2/11/12
to scalatest-users
Dear Bill
thank you for your answer.

trait ArrayWithToleranceMatcher {
    val tolerance:Double;
   
    implicit def matcher = new Matcher[Array[Double]]{
        // code
    }
}

class ArrayTest extends Suite with ArrayWithToleranceMatcher {
    val firstArray = Array.tabulate[Double]{ index => index * 0.99999 }
    val secondArray = Array.tabulate[Double] {index = index *1.0}
    val thirdArray  = Array.tabulate[Double] { index = > index * 0.99998}
   
    val tolerance = 1e-5;
   
    test("The matcher behaves correctly") {
        firstArray should be secondArray;
        secondArray should not be thirdArray;
    }
   
}

Something like matcher on double, but on arrays...

Best Regards
Edmondo



2012/2/11 edmondo1984 <edmond...@gmail.com>

Bill Venners

unread,
Feb 11, 2012, 7:51:13 AM2/11/12
to scalate...@googlegroups.com
Hi Edmondo,

Here's an interpreter session showing one way to do this:

scala> import org.scalatest._
import org.scalatest._

scala> import matchers.ShouldMatchers._
import matchers.ShouldMatchers._

scala> import matchers.Matcher
import matchers.Matcher

scala> import matchers.MatchResult
import matchers.MatchResult

scala> val firstArray = Array.tabulate[Double](6){ index => index -
0.00001 }.tail
firstArray: Array[Double] = Array(0.99999, 1.99999, 2.99999, 3.99999, 4.99999)

scala> val secondArray = Array.tabulate[Double](6){ index => index * 1.0 }.tail
secondArray: Array[Double] = Array(1.0, 2.0, 3.0, 4.0, 5.0)

scala> val thirdArray = Array.tabulate[Double](6){ index => index -
0.00002 }.tail
thirdArray: Array[Double] = Array(0.99998, 1.99998, 2.99998, 3.99998, 4.99998)

scala> val tolerance = 1e-5
tolerance: Double = 1.0E-5

scala> def pretty(a: Array[Double]) = a.mkString("Array(", ",", ")")
pretty: (a: Array[Double])String

scala> def equalWithTolerance(right: Array[Double], tol: Double) =
| Matcher { (left: Array[Double]) =>
| MatchResult(
| (left zip right) forall { case (a, b) => a <= b + tol &&
a >= b - tol },
| pretty(left) + " did not equal " + pretty(right) + " with
tolerance " + tol,
| pretty(left) + " equaled " + pretty(right) + " with
tolerance " + tol
| )
| }
equalWithTolerance: (right: Array[Double], tol:
Double)org.scalatest.matchers.Matcher[Array[Double]]

scala> firstArray should equalWithTolerance (secondArray, tolerance)

scala> firstArray should not (equalWithTolerance (secondArray, tolerance))
org.scalatest.TestFailedException:
Array(0.99999,1.99999,2.99999,3.99999,4.99999) equaled
Array(1.0,2.0,3.0,4.0,5.0) with tolerance 1.0E-5
at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:150)
...

scala> thirdArray should equalWithTolerance (secondArray, tolerance)
org.scalatest.TestFailedException:
Array(0.99998,1.99998,2.99998,3.99998,4.99998) did not equal
Array(1.0,2.0,3.0,4.0,5.0) with tolerance 1.0E-5
at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:150)
....

scala> thirdArray should not (equalWithTolerance (secondArray, tolerance))

You have to prettify the array toString because otherwise you get the
default Java toString. Possibly you'd want to make the error message
more descriptive by identifying the initial failing one, or all the
failing ones perhaps. Otherwise it might be hard to see which element
or elements are out of tolerance.

Bill

Edmondo Porcu

unread,
Feb 11, 2012, 8:09:05 AM2/11/12
to scalate...@googlegroups.com
Thank you...now it is much clearer... maybe it is the case to add a small part on the documentation...

"Two sides matchers can be implemented simply through a factory method, which receives the right side, plus other parameters, and create a matcher "

Example

def beOlderThan(file:File) =Matcher {
(left:File) => MatchResult(left.lastModified<file.lastModified, " The file is older","etcmessage,"...);

}

Thank you for your precious help

Best Regards

2012/2/11 Bill Venners <bi...@artima.com>

Bill Venners

unread,
Feb 11, 2012, 10:42:27 AM2/11/12
to scalate...@googlegroups.com
Hi Edmondo,

Yes, very good idea. I didn't realize such an example wasn't in the
docs. I'll add it.

Bill

Reply all
Reply to author
Forward
0 new messages