问题
I am running into a problem while writing a custom matcher for NodeSeq:
private def matchXML(expected: NodeSeq) = new Matcher[NodeSeq] {
def apply(left: NodeSeq): MatchResult = MatchResult(left xml_== expected,
"XML structure was not the same (watch spaces in tag texts)",
"XML messages were equal")
}
This compiles, but the following piece of code:
val expected : NodeSeq = ...
val xml : NodeSeq = ...
xml should matchXML(expected)
causes:
error: overloaded method value should with alternatives:
(beWord: XMLStripDecoratorTests.this.BeWord)XMLStripDecoratorTests.this.ResultOfBeWordForAnyRef[scala.collection.GenSeq[scala.xml.Node]] <and>
(notWord: XMLStripDecoratorTests.this.NotWord)XMLStripDecoratorTests.this.ResultOfNotWordForAnyRef[scala.collection.GenSeq[scala.xml.Node]] <and>
(haveWord: XMLStripDecoratorTests.this.HaveWord)XMLStripDecoratorTests.this.ResultOfHaveWordForSeq[scala.xml.Node] <and>
(rightMatcher: org.scalatest.matchers.Matcher[scala.collection.GenSeq[scala.xml.Node]])Unit
cannot be applied to (org.scalatest.matchers.Matcher[scala.xml.NodeSeq])
xml should (matchXML(expected))
Any ideas what this means?
回答1:
Why this fails to typecheck:
The type checker works in the following way.
xml.should(matchXML(expected))
- Because the method
should
is not part of aNodeSeq
, the compiler tries to find an implicit conversion forxml
to aShouldMatcher
. The book "Programming in Scala" specifies that such implicit conversion should be the most specific:
"Up through Scala 2.7, that was the end of the story. Whenever multiple implicit conversions applied, the compiler refused to choose between them. ... Scala 2.8 loosens this rule. If one of the available conversions is strictly more specific than the others, then the compiler will choose the more specific one. ... one implicit conversion is more specific than another if one of the following applies: The argument type of the former is a subtype of the latter’s. .."
Because
NodeSeq
extendsSeq[Node]
, the following functionconvertToSeqShouldWrapper[T](o : scala.GenSeq[T]) : SeqShouldWrapper[T]
is therefore the most specific one among all others.
The program is rewritten as:
`convertToSeqShouldWrapper(xml).should(matchXML(expected))`
where convertToSeqShouldWrapper(xml)
is a SeqShouldWrapper[T]
where T = GenSeq[Node]
.
The method should
from SeqShouldWrapper
accepts a Matcher[T]
which is a function of type T => MatchResult
. Therefore, it accepts a Matcher[GenSeq[Node]]
.
Because T
is appearing to the left of the arrow, matchers are not covariant in T
, but contravariant. A NodeSeq
is a GenSeq[Node]
, so a Matcher[GenSeq[Node]]
is a Matcher[NodeSeq]
, not the opposite.This explains the above error, where the method should
cannot accept a Matcher[NodeSeq]
and requires a Matcher[GenSeq[Node]]
.
2 Solutions
- Replace All instances of
NodeSeq
toGenSeq[Node]
so that the type matches everywhere. Alternatively, wrap xml explicitely with the conversion function.
convertToAnyShouldWrapper(xml).should(matchXML(expected))
回答2:
This looks to me like your matchXML
method is not in scope.
来源:https://stackoverflow.com/questions/16341868/scalatest-writing-custom-matchers