Unable to test Silhoutte. Getting null pointer when mocking CredentialProvider

#1

My controller has an Action signInUser which is called when a user send a login request. In the Action, I retrieve username and password from json body of the request and call CredentialProvider

def signInUser = silhouette.UserAwareAction.async { implicit request => { 

    val body: AnyContent = request.body
    val jsonBody: Option[JsValue] = body.asJson

    jsonBody match {
      case Some(json) => {
        val readableString: String = Json.prettyPrint(json)
        val userSigninOption: Option[UserSigninAPI] = json.asOpt[UserSigninAPI] //check if json conforms with UserProfile structure
        userSigninOption match {
            case Some(signinInfo) => { //format of JSON is correct
              val credentials: Credentials = Credentials(signinInfo.signinInfo.email, signinInfo.signinInfo.password) //TODOM  need to check that password is not empty
              val authInfoRepository = new DelegableAuthInfoRepository(userRepo.passwordRepo)
              val passwordHasherRegistory = new PasswordHasherRegistry(userRepo.passwordHasher)
              val credentialsProvider = new CredentialsProvider(authInfoRepository, passwordHasherRegistory)
              val loginInfoFuture: Future[LoginInfo] = credentialsProvider.authenticate(credentials) 

loginInfoFuture.flatMap(loginInfo => {...}
.recover { case x => {
                  println("Future failed in signIn User. In recover. Returning Internal Server Error" + x)
                  x match {
                    case e:InvalidPasswordException =>{
                      Unauthorized(Json.toJson(JsonResultError("Incorrect username or password")))
                    }
                    case _ => {
                      InternalServerError(Json.toJson(JsonResultError("Internal Server Error. Reason "+x)))
                    }
                  }

                }
              
...

I want to unit test the code. My unit test is crashing because the loginInfoFuture gets NullPointerException.

Some snippets from my unit test are

class TestEnv {
val mockAuthInfoRepository = mock(classOf[DelegableAuthInfoRepository])
  val mockPasswordHasherRegistory = mock(classOf[PasswordHasherRegistry])
  val mockCredentialsProvider = mock(classOf[CredentialsProvider])
  ....
implicit val fakeEnv = FakeEnvironment[JWTEnv](Seq(loginInfo -> user))
  val defaultParser = new mvc.BodyParsers.Default()
  val securedAction = new DefaultSecuredAction(new DefaultSecuredRequestHandler(new DefaultSecuredErrorHandler(stubMessagesApi())), defaultParser)
  val unsecuredAction = new DefaultUnsecuredAction(new DefaultUnsecuredRequestHandler(new DefaultUnsecuredErrorHandler(stubMessagesApi())), defaultParser)
  val userAware = new DefaultUserAwareAction(new DefaultUserAwareRequestHandler(), defaultParser)
  val fakeSilhouette = new SilhouetteProvider[JWTEnv](fakeEnv, securedAction, unsecuredAction, userAware)
}

  "signInUser" should {
    "sign in user with correct details" in {
      val signInRequestJson = Json.parse(
        """
          |{
            |"signin-info": {
            |"email":"test@test.com",
            |"password":"aA1!1111"
            |}
          |}
        """.stripMargin
      )
      val request = FakeRequest("POST","ws/users/signin").withJsonBody(signInRequestJson)
      println(s"will send request ${request} ")

      val testEnv = new TestEnv(components.configuration)
    //  println(s"mocked identity service of silhouette is ${testEnv.fakeSilhouette.env.identityService}")
      when(testEnv.mockCredentialsProvider.authenticate(ArgumentMatchers.any[Credentials])).thenReturn(
        Future{
          println(s"mocked credentials provider will return ${testEnv.loginInfo}")
          testEnv.loginInfo}
      )

      val resultFuture = testEnv.controller.signInUser(request)
      val result = contentAsJson(resultFuture)
      println(s"received result ${result}")
      1 mustBe 1
    }
  }

The stack trace is

java.lang.NullPointerException
	at com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository.$anonfun$find$1(DelegableAuthInfoRepository.scala:51)
	at com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository.$anonfun$find$1$adapted(DelegableAuthInfoRepository.scala:51)

0 Likes

#2

You should use Mockito.RETURNS_SMART_NULLS to get better hints what’s going wrong.

Something like:

mock(classOf[DelegableAuthInfoRepository], RETURNS_SMART_NULLS)
0 Likes

#3

The issue was I wan’t using Mockito correctly. In my code, I am creating a new CredentialsProvider. I misunderstood that if I mock a class then any call to new will create a mock instead. This is not how mockito works. In the end, I had to inject an instance of CredentialsProvider in my controller. I my test, created mock of CredentialsProvider and injected it in the controller.

0 Likes