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

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?

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

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")

Related

How do I use ScalaTest "inside" with an Array?

I have a case class and am trying to test similar to this. The change would be something like this...
case class Record(names: Array[Name] ...)
I am new to Scala and not sure how this would work syntactically
Please consider the following code:
case class Name(first: String, middle: String, last: String)
case class Record(names: Array[Name])
val rec = Record(
Array(Name("Sally", "Anna", "Jones"), Name("Sally1", "Anna1", "Jones1"))
)
inside (rec) { case Record(nameArray) =>
inside (nameArray) { case Array(name, name1) =>
inside(name) {
case Name(first, middle, last) =>
first should be("Sally")
middle should be("Anna")
last should be("Jones")
}
inside(name1) {
case Name(first, middle, last) =>
first should be("Sally1")
middle should be("Anna1")
last should be("Jones1")
}
}
}
Note that if the number of names at case Array(name, name1) is different then the actual, the test will fail.
As Luis mentioned in the comment, it is not recommended to use Arrays in case classes. This code will work the same if you change Array into List, Vector or ArraySeq.

Is it possible to replicate behavior of the "exists" method using pattern matching in Scala?

Suppose I have a Seq(1, 2, 3, 4, 5) and I want to know if value "4" exists in it. I want to know if it is possible to find and extract the answer only using pattern matching.
Seq(1, 2, 3, 4, 5) match {
case Seq(_, 4, _*) => true
case _ => false
}
match clauses like this only works if the position of our specified value is known. I am curious to know if it is possible to do this in Scala, I am new to Scala and not completely familiar with its syntax and features.
obviously using the if gaurd is not helpful because it does not allow extracting the value to a variable.
you may ask why it is even important and why not use current existing exists or filter methods. Imagine the case below.
case class Score(eventName: String, amount: Integer)
case class Person(name: String, scores: Seq[Score])
suppose I have a collection of Persons and I want to find the highest score for a certain event. a person may have a score for that event or may not.
persons.collect({
case x # _ if x.scores.exists(_.eventName == "event1") =>
x.scores.find(_.eventName == "event1").get.amount
}).max
the problem is that the filter on eventName is processed two times.
I know it is possible to do this using filter and map with mutiple iterations. but I am interested to know if there is a solution using pattern matching, with pattern matching you can find and extract the value to a variable at the same time.
You can write a custom matcher with an unapply method:
case class EventScore(eventName: String) {
def unapply(person: Person): Option[Int] =
person.scores.collectFirst { case s if s.eventName == eventName => s.amount }
}
val score = EventScore("event1")
persons.collect({
case score(amount) => amount
}).max
You might be able to do it by pattern matching inside a recursive method but I think this is all you need:
persons.flatMap(_.scores.filter(_.eventName == "ev1")
.map(_.amount))
.maxOption
.getOrElse(0)
Or the prettier for comprehension:
(for {
person <- persons
score <- person.scores
if score.eventName == "ev1"
} yield score.amount).maxOption.getOrElse(0)
//using only pattern matching
def has(xs: Seq[Int], target: Int) : Boolean = xs match {
case Seq() => false
case Seq(`target`, _ #_*) => true
case Seq(_, tl #_*) => has(tl, target)
}
testing:
has(List(2,4,5,11,44,66), 11) //true
has(List(2,4,5,11,44,66), 12) //false
has(List(2,4,5,11,44,66), 66) //true
There is no way currently to introduce term inside guard part of pattern matching (I mean if ... part). I was looking for something like that but without luck.
In your example tho you don't need it. Few words about example:
Using two brackets is not necessary collect({..}).
# _ in case is not necessary either
.max will blow on empty collection. It is especially risky with collect
Here is my proposition:
persons.map {
case x => x.scores.find(_.eventName == "event1").getOrElse(0)
}.max

Scala methods with generic parameter type

I have been working with Scala for close to a year, but every now and then I come across a piece of code that I don't really understand. This time it is this one. I tried looking into documents on "scala methods with generic parameter type", but I am still confused.
def defaultCall[T](featureName : String) (block : => Option[T])(implicit name: String, list:Seq[String]) : Option[T] =
{
val value = block match {
case Some(n) => n match {
case i : Integer => /*-------Call another method----*/
case s : String => /*--------Call another method----*/
}
case _ => None
}
The method is called using the code shown below :
var exValue = Some(10)
val intialization = defaultCall[Integer]("StringName"){exValue}
What I don't understand in the above described code is the "case" statement in the defaultCall method.
I see that when the exValue has a value and is not empty, the code works as expected. But in case I change the exValue to None, then my code goes into the "case _ = None" condition. I don't understand why this happens since the match done here is against the "variable" which would be either an Integer or a String.
What happens here is that when you pass a None it will match on the second case, which "catches" everything that is not an instance of a Some[T]:
block match {
case Some(n) => // Will match when you pass an instance of Some[T]
case _ => // Will match on any other case
}
Note that None and Some are two different classes that inherit from Option.
Also, the variable match is only done if the first match succeeds, otherwise not. To achieve the type checking in the first match you could do:
block match {
case Some(n: Int) => // do stuff
case Some(n: String) => // do stuff
case _ => // Will match on any other case
}
Hope that helps

How to map an Option case class

Say, there is a case class
case class MyCaseClass(a: Int, b: String)
and an Option[MyCaseClass] variable
val myOption: Option[MyCaseClass] = someFunctionReturnOption()
Now, I want to map this Option variable like this:
myOption map {
case MyCaseClass(a, b) => do some thing
}
It seems the compiler reports error like It needs Option[MyCaseClass], BUT I gave her MyCaseClass, bla bla... How to use pattern match in Optional case class ?
Consider extracting the Option value like this,
myOption map {
case Some(MyCaseClass(a, b)) => do some thing
case None => do something else
}
or else use collect for a partial function, like this
myOption collect {
case Some(MyCaseClass(a, b)) => do some thing
}
Update
Please note that as commented, the OP code is correct, this answer addresses strictly the last question How to use pattern match in Optional case class ?
MyOption match {
Some(class) => // do something
None => // do something.
}
Or
MyOption map (class =>//do something)

Pattern match for the existence of an attribute present in a case class

Here is an example case class:
case class Person( firstName: Either[Unit, String],
middleName: Either[Unit, Option[String],
lastName: Either[Unit,String])
Any time I get an instance of this case class with a middleName it is invalid and I want to do something, all other cases are ok.
EDIT
To clarify. I need to guard against using an instance of this case class in a certain method if it was constructed with a middleName. So I would want to do something like this:
person match {
case Person(_,m,_) => halt()
case _ => continue()
}
I'm just having a hard time thinking about the types involved here.
Your pattern matching does not test the actual value of middleName, it simply assigns it to m. All Person instances are gonna match this first case.
If you want to call halt if middleName is a Right for example you should write:
person match {
case Person(_, Right(_), _) => halt()
case _ => continue()
}
If you want to dive into the value of Right to see if it's a Some:
person match {
case Person(_, Right(Some(_)), _) => halt()
case _ => continue()
}