What is the correct way to test a function which returns a Try[T]

22 views
Skip to first unread message
Message has been deleted

Mithun Raj Arackal

unread,
Feb 19, 2020, 11:34:16 AM2/19/20
to scalatest-users
What is the best way to test a function which returns a `Try[T]` for positive and negative test cases. The way I'm doing it now is as given below:

 
def test(a: Int, b: Int): Try[Int] = Try {
     a / b
  }

  it.should("succeed").in {
   test(0, 1).map { res =>
     assert(res === 1 / 2)
     /* assume more assertions are to be done and assertions can't be moved out */
   } match {
     case Success(value)     => value
     case Failure(exception) => fail(exception)
   }
 }

  it.should("fail").in {
   test(1, 0).failure.exception.getClass.mustBe(classOf[java.lang.ArithmeticException])
 }

The problem with above approach is that, for the success case if any issue happens in the test case it'll show the error at the line `case Failure(exception) => fail(exception)` and not on the line where the actual error occurred. If the test case is huge then it'll be difficult for the user to find where exactly the error occurred.

Manoj Waikar

unread,
Mar 3, 2020, 9:22:00 AM3/3/20
to scalatest-users
I am not sure why you are trying to test it the way you've shown. You're trying to test it in a more Java-ish fashion, however, using matchers, you can test it like below -

Enter code here...class MiscTest extends WordSpecLike with Matchers {

  "Divide" should {

    "succeed" in {
      val result = divide(42)
      result.map { res => res should be(2) }
    }

    "fail" in {
      val result = divide(10)
      result.map { res => res should be(an[ArithmeticException]) }
    }
  }

  def divide(a: Int, b: Int): Try[Int= Try {
    a / b
  }
}

Does this solve your problem?

Mithun Raj Arackal

unread,
Mar 3, 2020, 12:54:13 PM3/3/20
to scalatest-users
I tried the above test case, but the assertions are not getting checked. Even if the assertion is wrong the test case would still show it as succeeded. I exchanged the inputs of the success and failure cases and still the unit test succeeded for both the cases.
Message has been deleted

Manoj Waikar

unread,
Mar 4, 2020, 4:57:21 AM3/4/20
to scalatest-users
Oh, I didn't know this behavior, you are right. I tried out your suggestion and am shocked. So a simple way is to convert a Try to an Either -

"Divide" should {

    "succeed" in {
     val result = divide(4, 2).toEither
     result should be('right)
     result.right.get should be(2)
   }

    "fail" in {
     val result = divide(1, 0).toEither
     result should be('left)
     result.left.get should be(an[ArithmeticException])
   }
 }

This works and if you exchange the inputs, then both tests will fail.

Mithun Raj Arackal

unread,
Mar 4, 2020, 5:36:55 AM3/4/20
to scalatest-users
I think we can use `import org.scalatest.TryValues._` and use this approach. Advantage is you don't need to convert to either.

But my problem is that the existing testing code was based on methods returning `Future`; but now all of those methods are a `Try`. So whatever assertions that was done within the `.map` is not working now unless you convert the `Try[Assertion]` to `Assertion`. I have given an example of how I am doing it in the success case. Moving the assertions out of the `.map` would need more work. For now I think I'll go with this approach and then later move all the assertions outside the `.map`.

The failure case uses `TryValues` and can be further optimized by just matching on the exception class as you've specified and not relying on `getClass()` or `classOf`.
Reply all
Reply to author
Forward
0 new messages