Martin Epsz
Martin Epsz

Reputation: 872

Complex custom Matcher

I am writing tests for the Json output of some API calls in my API, written with Play on Scala. In my tests, this pattern keeps appearing, and I would like to deduplicate it.

val response = sut.index()(FakeRequest())
val expected = Json.parse("""{ "channels":[] }""")

status(response) must equalTo(OK)
contentType(response) must beSome.which(_ == "application/json")
contentAsJson(response) mustEqual expected

My first approach was this:

def assertSameJson(response: Future[Result], expected: JsValue): Unit = {
  status(response) must equalTo(OK)
  contentType(response) must beSome.which(_ == "application/json")
  contentAsJson(response) mustEqual expected
}

But this does not feel idiomatic at all. Is seems like I am adding xUnit asserts in my specs

I would like something leading to

response must beSameJson(expected)

The closest thing I managed was

def beSameJson(other:Any) = 
  be_==(other) ^^ ((t: Future[Result]) => contentAsJson(t)) and
  be_==(OK) ^^ ((t: Future[Result]) => status(t))

But this does not check for the content-type, and I feel it's just very hard to read. Is there a better way to write this Matcher?

Upvotes: 0

Views: 146

Answers (1)

dth
dth

Reputation: 2337

I don't think there is a better way to do that. The ^^ operator is exactly there for this purpose to transform the information before applying an other matcher. and can be used to combine more than two matchers.

So the only thing you could do is to try to write it a bit cleaner:

def beSameJson(data: String) = 
  equalTo(OK) ^^ {status(_: Future[Result])}
  and beSome.which(_ == "application/json")  ^^ {contentType(_: Future[Result])}
  and be_==(other) ^^ {contentAsJson(_: Future[Result])}

If you need to decompose responses more often, you could try to do this more generically

object Dummy extends Matcher[Any] {
  def apply[S <: Any](s: Expectable[S]) = {
    result(true,
      s.description + " is ignored",
      s.description + " is ignored",
      s)
  }
}

def beResponseWhere(json: Matcher[JsValue] = Dummy, stat: Matcher[Int] = Dummy, tpe: Matcher[Option[String]] = Dummy) =
  stat ^^ {status(_: Future[Result])}
  and tpe ^^ {contentType(_: Future[Result])}
  and json ^^ {contentAsJson(_: Future[Result])}
}

You should probably use nicer parameter names (I tried to avoid conflict with the methods from your context for this example) and be more complete on the available attributes.

Now you should be able to write something like this:

response must beResponseWhere(
  json = equalTo(expected),
  tpe = beSome.which(_ == "application/json"),
  stat = equalTo(OK)
)

The DummyMatcher allows you to leave some parts out. I obviously did not try this code as I do not have your complete setting. I also had to guess some types that are not clear from your code snippet.

Upvotes: 1

Related Questions