Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concise way to assert a value matches a given pattern in ScalaTest

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?

like image 412
Alexey Romanov Avatar asked Dec 03 '14 17:12

Alexey Romanov


People also ask

Does Scala have pattern matching?

Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

What is default assertion in Scala?

You can use: assert for general assertions; assertResult to differentiate expected from actual values; assertThrows to ensure a bit of code throws an expected exception.

What is FlatSpec in Scala?

The main premise behind the FlatSpec trait is to help facilitate a BDD style of development. It's named flat because the structure of the tests we write is unnested in nature. In addition, this trait tries to guide us into writing more focused tests with descriptive, specification-style names.


2 Answers

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

like image 171
Bill Venners Avatar answered Oct 21 '22 14:10

Bill Venners


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")
like image 31
Daryl Odnert Avatar answered Oct 21 '22 14:10

Daryl Odnert