1

I'm using Play Framework for ease in JSON parsing in Scala. My code looks like:

def getAuthToken(userid: String, password: String): String = {
    val body: JsValue = Json.obj("userid" -> userid , "password" -> password)

    val res = Json.parse(Http("https://<some url>")
        .postData(body.toString()).asString.body)

    res("token").toString()
}

Now if I provide proper userid and password, I'm sure to get a token inside my res object. So I'm writing a test case in Scala which currently looks like this, where I don't pass a valid username and password:

class ApiCheckerTest extends FunSuite {
    test("ApiChecker.getAuthToken") {
        assert(ApiChecker.getAuthToken("" , "") == null)
    }
}

When I try to run this, I get this exception raised:

token
java.util.NoSuchElementException: token
    at play.api.libs.json.JsLookup$.apply$extension1(JsLookup.scala:68)
    at ApiChecker$.getAuthToken(ApiChecker.scala:15)
    at ApiCheckerTest$$anonfun$1.apply(ApiCheckerTest.scala:5)
    at ApiCheckerTest$$anonfun$1.apply(ApiCheckerTest.scala:5)
    at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
    at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
    at org.scalatest.Transformer.apply(Transformer.scala:22)
    at org.scalatest.Transformer.apply(Transformer.scala:20)
    at org.scalatest.FunSuiteLike$$anon$1.apply(FunSuiteLike.scala:186)
    at org.scalatest.TestSuite$class.withFixture(TestSuite.scala:196)
    at org.scalatest.FunSuite.withFixture(FunSuite.scala:1560)
    at org.scalatest.FunSuiteLike$class.invokeWithFixture$1(FunSuiteLike.scala:183)
    ...

ApiChecker$.getAuthToken(ApiChecker.scala:15) is the last line of the getAuthToken function.

How do I test whether I have token present in res or not?

cchantep
  • 8,797
  • 3
  • 25
  • 39
Sparker0i
  • 1,464
  • 3
  • 24
  • 45

1 Answers1

2

res("token") is same as res.apply("token")

You have a res which is of type JsValue. And JsValue has an apply method that takes in a String and returns with another JsValue. Now the problem with this apply method is that it can also throw an exception, here have a look at the method

/**
   * Access a value of this array.
   *
   * @param fieldName Element index.
   */
  def apply(fieldName: String): JsValue = result match {
    case JsDefined(x) => x match {
      case arr: JsObject => arr.value.lift(fieldName) match {
        case Some(x) => x
        case None => throw new NoSuchElementException(String.valueOf(fieldName))
      }
      case _ =>
        throw new Exception(x + " is not a JsObject")
    }
    case x: JsUndefined =>
      throw new Exception(String.valueOf(x.error))
  }

But the function you wrote, only returns with a String and let's the exception bubble out.

def getAuthToken(userid: String, password: String): String

So there are two ways of going forward, either you change the return type to also indicate that your function can error out

def getAuthToken(userid: String, password: String): Try[String] = {
    val body: JsValue = Json.obj("userid" -> userid , "password" -> password)

    val res = Json.parse(Http("https://<some url>")
        .postData(body.toString()).asString.body)

    Try(res("token").toString())
}

Or, keep the same setup and invoke your method only inside a try catch block.

I would say go with the Try[String] return type or if you are only concerned with the token being present or not, use the Option[String] return type. Here's a sample

def getAuthToken(userid: String, password: String): Option[String] = {
    val body: JsValue = Json.obj("userid" -> userid , "password" -> password)

    val res = Json.parse(Http("https://<some url>")
        .postData(body.toString()).asString.body)

    Try(res("token").toString()).toOption
}

This makes your code safer and easier to test, now you can just check that the result is None instead of Some(token).

Gagandeep Kalra
  • 876
  • 10
  • 12