Hannes
Hannes

Reputation: 5262

How to check an "Either" result in Scala Test?

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

Upvotes: 10

Views: 6593

Answers (5)

M. Justin
M. Justin

Reputation: 21123

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

Upvotes: 1

Mario Galic
Mario Galic

Reputation: 48410

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

Upvotes: 7

Pepster
Pepster

Reputation: 2166

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

Upvotes: 2

Yuriy Tumakha
Yuriy Tumakha

Reputation: 1570

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

}

Upvotes: 9

pme
pme

Reputation: 14803

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
    }
  }
}

Upvotes: 5

Related Questions