Implicit conversion in ArgThat causes incorrect matching
Closed this issue · 3 comments
Using specs2-core and specs2-mock 4.20.3 the following passes:
import org.specs2.mock.mockito.ArgThat
import org.specs2.mutable.Specification
class ArgThatBugSpec extends Specification with ArgThat {
val items: Seq[String] = Seq()
"Things" should {
"Fail 1" in {
items must have size greaterThan(1000)
items must have size lessThan(-1000)
}
"Fail 2" in {
items must haveSize(greaterThan(1000))
items must haveSize(lessThan(-1000))
}
}
}
Remove ArgThat and the tests fail as expected.
Example project here https://github.com/nespera/argThat-bug
Hi @nespera. That's an interesting corner case with implicit conversions. haveSize
accepts both an Int
and a ValueCheck[T]
. Then there are 2 possible implicit conversions to go from Matcher[T]
to Int
(in ArgThat
) and Matcher[T]
to ValueCheck[T]
(in org.specs2.matcher.ValueCheck
).
Unfortunately it seems that one of the two conversions has to give. I couldn't find a way to make both work at the same time.
One solution is to use the ArgThat
object in a specific scope, just where needed:
{
import ArgThat._
val myMock = mock[MyClass]
myMock.executeMethod(greaterThan(1))
}
items must haveSize(greaterThan(1000))
Yes, I thought this might be difficult to fix. It is definitely a corner case, but one that confused me when I found it. In practice it's hard to avoid ArgThat if you're using the classes in org.specs2.mock. For example the trait org.specs2.mock.Mockito includes it via 3 different routes...
There is a workaround
trait NoArgThat extends Mockito {
override def argThat[T, U <: T](m: org.specs2.matcher.Matcher[U]): T =
super.argThat(m)
}
If you mix-in that trait, it should deactivate the argThat
conversion. But then you lose the implicit conversion and you have to explicitly use argThat
when using a matcher on mock calls.