14

Is there a nice way to check that a pattern match succeeds in ScalaTest? An option is given in scalatest-users mailing list:

<value> match {
  case <pattern> =>
  case obj => fail("Did not match: " + obj)
}

However, it doesn't compose (e.g. if I want to assert that exactly 2 elements of a list match the pattern using Inspectors API). I could write a matcher taking a partial function literal and succeeding if it's defined (it would have to be a macro if I wanted to get the pattern in the message as well). Is there a better alternative?

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487

2 Answers2

10

I am not 100% sure I understand the question you're asking, but one possible answer is to use inside from the Inside trait. Given:

case class Address(street: String, city: String, state: String, zip: String)
case class Name(first: String, middle: String, last: String)
case class Record(name: Name, address: Address, age: Int)

You can write:

inside (rec) { case Record(name, address, age) =>
  inside (name) { case Name(first, middle, last) =>
    first should be ("Sally")
    middle should be ("Ann")
    last should be ("Jones")
  }
  inside (address) { case Address(street, city, state, zip) =>
    street should startWith ("25")
    city should endWith ("Angeles")
    state should equal ("CA")
    zip should be ("12345")
  }
  age should be < 99
}

That works for both assertions or matchers. Details here:

http://www.scalatest.org/user_guide/other_goodies#inside

The other option if you are using matchers and just want to assert that a value matches a particular pattern, you can just the matchPattern syntax:

val name = Name("Jane", "Q", "Programmer")
name should matchPattern { case Name("Jane", _, _) => }

http://www.scalatest.org/user_guide/using_matchers#matchingAPattern

The scalatest-users post you pointed to was from 2011. We have added the above syntax for this use case since then.

Bill

tlegutko
  • 698
  • 2
  • 11
  • 16
Bill Venners
  • 3,549
  • 20
  • 15
1

This might not be exactly what you want, but you could write your test assertion using an idiom like this.

import scala.util.{ Either, Left, Right }
// Test class should extend org.scalatest.AppendedClues

val result = value match {
  case ExpectedPattern => Right("test passed")
  case _ => Left("failure explained here") 
})
result shouldBe 'Right withClue(result.left.get)

This approach leverages the fact that that Scala match expression results in a value.

Here's a more concise version that does not require trait AppendedClues or assigning the result of the match expression to a val.

(value match {
  case ExpectedPattern => Right("ok")
  case _ => Left("failure reason")
}) shouldBe Right("ok")
Daryl Odnert
  • 522
  • 3
  • 15