How to check an "Either" result in Scala Test?
Asked Answered
F

5

10

I am relatively new to Scala Test, and so I consulted the documentation on how to test on Either values.

I tried to replicate the instructions like that:

import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec

class EitherTest extends AnyFlatSpec with EitherValues {
  val either: Either[Exception, Int] = Right(42)

  either.right.value should be > 1
}

This implementation does not do the trick, I get a syntax error. What did I do wrong?

Error:

Error:(9, 22) value should is not a member of Int
   either.right.value should be > 1
Error:(9, 29) not found: value be
   either.right.value should be > 1
Fonda answered 21/12, 2019 at 22:50 Comment(7)
what is the error you get? can you add that as well pleaseKalimantan
Error:(9, 22) value should is not a member of Int either.right.value should be > 1 Error:(9, 29) not found: value be either.right.value should be > 1Fonda
Can you try removing the explicit type signature from Either and see if that works? val either = Right(42) Kalimantan
@Hannes: I think you're missing the mix-in Matchers (eg class EitherTest extends AnyFlatSpec with EitherValues with Matchers).Rhyne
@Rhyne mixing-in org.scalatest.matchers.should.Matchers got rid of the syntax error but my IDE still states that Either.right is deprecated since Scala 2.13.0, Is that expected?Fonda
Yes Either.right is deprecated as it is right oriented. either.contains or either.exists should work for you.Colonialism
A workaround I found is using with OptionValues instead and then either.toOption.value.Zeculon
S
9

Testing Either by matching with pattern can be more readable. ScalaTest's Inside trait allows you to make assertions after a pattern match.

import org.scalatest.Inside
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class EitherTest extends AnyFlatSpec with Inside with Matchers {
  val either: Either[Exception, Int] = Right(42)

  either should matchPattern { case Right(42) => }

  inside(either) { case Right(n) =>
    n should be > 1
  }

  val either2: Either[Exception, Int] = Left(new Exception("Bad argument"))

  inside(either2) { case Left(e) =>
    e.getMessage should startWith ("Bad")
  }

}
Slr answered 22/12, 2019 at 18:1 Comment(2)
Thank you for this code snippet, I will give it a try! But I've got two questions: 1) What is that => } syntax? I know the match syntax but I did not know that you can leave out the right part of the expression. 2) Can matchPattern be used together with the "Either" should matchPattern in { ...} syntax?Fonda
@Fonda 1. right part is empty Unit without any implementation. We have only checking either matched with the pattern. 2. More details about matching a pattern in Scalatest docs scalatest.org/user_guide/using_matchers#matchingAPatternSlr
R
7

There was an open pull request Add EitherValuable #1712 which aimed to address the fact that in Scala 2.13 the RightProjection is deprecated:

at present .right is deprecated (and I believe it will stay that way) but .left isn't, as per scala/scala#8012

However, this pull request was closed without accepting it, so it will not become part of Scala. Per the author's closing comments:

I lost interest in this

If this pull request had been accepted, the new syntax of EitherValues in future ScalaTest versions might have become rightValue and leftValue like so

either.rightValue should be > 1
Revareval answered 22/12, 2019 at 18:45 Comment(1)
This has been closed without merging, and with the following comment: "I lost interest in this". I've updated this answer to indicate that this approach will not work.Covering
D
5

Here is how I would do it.

Check first if it is right and then compare the value:

either.isRight shouldBe true
either.getOrElse(0) shouldBe 42

Another way is to fail in case it is not right:

either.getOrElse(fail("either was not Right!")) shouldBe 42

I also would wrap your test, for example as WordSpec:

"My Either" should {
  "be Right" in {
    either.getOrElse(fail("either was not Right!")) shouldBe 42
  }
}

This hints you where the problem is. Otherwise, if it fails, all you get is a nasty error stack.

And here the whole example:

class EitherTest
  extends WordSpec
  with Matchers
  with EitherValues {

  // val either: Either[Exception, Int] = Right(42)
  val either: Either[Exception, Int] = Left(new IllegalArgumentException)
  "My Either" should {
    "be Right" in {
      either.getOrElse(fail("either was not Right!")) shouldBe 42
    }
  }
}
Diadiabase answered 22/12, 2019 at 10:24 Comment(2)
The shouldBe appears not to exist. Is there another mix-in I need?Fonda
I think you missed the with Matchers see my updated answerDiadiabase
A
2

Did you consider the inside trait? That way you can test for the value inside your Either using pattern matching. For example:

import org.scalatest.Inside.inside
...
val result = FooBarInputValidator(value)
inside(result) { case Left(validationError) => 
   validationError.message shouldBe "Invalid input"
   validationError.parameter shouldBe "FooBar"
}
Allowable answered 13/1, 2023 at 16:1 Comment(0)
C
1

The code isn't compiling since the class is missing the Matchers trait, which is necessary in order to use the "should" DSL API. Adding with Matchers to the class declaration fixes the compilation error.

import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class EitherTest extends AnyFlatSpec with EitherValues with Matchers {
  val either: Either[Exception, Int] = Right(42)

  either.right.value should be > 1
}

Additionally, since right is deprecated since Scala 2.13.0, the value method can be directly called on the Either to avoid the deprecated member usage and associated warning, instead of using either.right.value.

  either.value should be > 1
Covering answered 26/7 at 5:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.