Syntactic Sugar for Common Scenarios
Learn how to leverage ScalaTest’s syntactic sugar to test Scala’s Option, Either, and PartialFunction.
We'll cover the following...
In this lesson, we’ll look into some syntactic sugar to make assertions on Option
, Either
, and partial functions. So far, we’ve frequently had to deal with Option
and Either
. In those cases, we often relied on pattern matching or explicit if
expressions. There’s nothing wrong with using pattern matching or if
in those cases, but in this lesson, we’ll analyze the idiomatic ScalaTest method.
Assertions on Option
and Either
The simplest way to assert that an instance of Option[T]
contains a valid value, different than None
, is simply calling .get
on it (line 16).
package mdipirro.educative.io.effectiveunitandintegrationtestinginscala.matchers import mdipirro.educative.io.effectiveunitandintegrationtestinginscala.TestSuite import mdipirro.educative.io.effectiveunitandintegrationtestinginscala.model.v2.{Author, Educative, FreeCourse, Lesson} import org.scalatest.Inspectors class EducativeSpec extends TestSuite: private val educative = Educative(Seq( FreeCourse("Scala for Beginners", Author("John", "Doe"), Set.empty[Lesson], Set.empty[String]), FreeCourse("Scala for Data Analysis", Author("Mary", "Jane"), Set.empty[Lesson], Set("Scala")), FreeCourse("Functional Kotlin", Author("Mary", "Jane"), Set.empty[Lesson], Set("kotlin")) )) it `should` "return a valid course, if it exists" in { val c = educative.courseByName("Scala for Beginners").get c.title shouldBe "Scala for Beginners" c.author.firstName shouldBe "John" c.author.lastName shouldBe "Doe" c.lessons shouldBe empty c.tags shouldBe empty c.price shouldBe 0 }
This is not ideal. If the returned Option
is None
, the error returned by ScalaTest contains very little useful information.
testOnly mdipirro.educative.io.effectiveunitandintegrationtestinginscala.matchers.EducativeSpec[info] EducativeSpec:[info] Educative[info] - should return a valid course, if it exists *** FAILED *** (1 millisecond)[info] java.util.NoSuchElementException: None.get[info] at scala.None$.get(Option.scala:627)[info] at scala.None$.get(Option.scala:626)[info] at mdipirro.educative.io.effectiveunitandintegrationtestinginscala.matchers.EducativeSpec.testFun$proxy6$1(EducativeSpec.scala:57)[info] at mdipirro.educative.io.effectiveunitandintegrationtestinginscala.matchers.EducativeSpec.$init$$$anonfun$6(EducativeSpec.scala:56)
Even if the stack trace points us to a line in the test suite class, the error message is not so clear: java.util.NoSuchElementException: None.get
. This is because the exception isn’t a TestFailedException
but a NoSuchElementException
.
We can make it more explicit using pattern matching or, better still, a fold
(line 16).
package mdipirro.educative.io.effectiveunitandintegrationtestinginscala.matchers import mdipirro.educative.io.effectiveunitandintegrationtestinginscala.TestSuite import mdipirro.educative.io.effectiveunitandintegrationtestinginscala.model.v2.{Author, Educative, FreeCourse, Lesson} import org.scalatest.Inspectors class EducativeSpec extends TestSuite: private val educative = Educative(Seq( FreeCourse("Scala for Beginners", Author("John", "Doe"), Set.empty[Lesson], Set.empty[String]), FreeCourse("Scala for Data Analysis", Author("Mary", "Jane"), Set.empty[Lesson], Set("Scala")), FreeCourse("Functional Kotlin", Author("Mary", "Jane"), Set.empty[Lesson], Set("kotlin")) )) it `should` "return a valid course, if it exists" in { educative.courseByName("Scala for Beginners").fold(fail("A valid course was expected")) { c => c.title shouldBe "Scala for Beginners" c.author.firstName shouldBe "John" c.author.lastName shouldBe "Doe" c.lessons shouldBe empty c.tags shouldBe empty c.price shouldBe 0 } }