Manu Chadha
Manu Chadha

Reputation: 16729

FakeRequest seem to be Null when passed to a controller in unit test

Getting Null Pointer error when unit testing controller. The issue seem to be in the line

def signupUser = Action.async{
    implicit request => { //requeust seem to be null

I suspect so because the stacktrace from previous tests point to implicit request line. But I don’t know what could be wrong in this because I am using FakeRequest like so val request = FakeRequest("POST", "/ws/users/signup").withJsonBody(Json.parse("""{"bad": "field"}"""))

Following is a snippet of a controller I want to unit-test

  class UserController @Inject()(userRepo: UsersRepository,cc: ControllerComponents, silhouette: Silhouette[JWTEnv])(implicit exec: ExecutionContext) extends AbstractController(cc){ 

def signupUser = Action.async{
    implicit request => {...}
 }

I only want to test that the controller returns an error when it gets a request without json body. Thus I don't need Silhouette and I want to mock it. But I am getting null pointer error.

Following is the way I have written my unit test case is

class UserControllerUnitSpec extends PlaySpec with MockitoSugar {

  "User signup request with non-JSON body" should {

    "return  400 (Bad Request) and the validation text 'Incorrect body type. Body type must be JSON'" in {

      val email = "[email protected]"
      val loginInfo = LoginInfo(CredentialsProvider.ID, email);
      val passwordInfo = PasswordInfo("someHasher","somePassword",Some("someSalt"))
      val internalUserProfile = InternalUserProfile(loginInfo,true,Some(passwordInfo))
      val externalUserProfile = ExternalUserProfile(email,"d","d",Some("somePassword"))
      val userProfile = UserProfile(Some(internalUserProfile),externalUserProfile)
      val user = User(UUID.randomUUID(),userProfile)

      println("testing with mocked User value",user);

      val mockUserRepository = mock[UsersRepository]
      when(mockUserRepository.findUser(loginInfo)).thenReturn(Future(Some(user)))
      when(mockUserRepository.saveUser(user)).thenReturn(Future(Some(user)))

      val mockSilhouette = mock[Silhouette[JWTEnv]] //I am probably not doing this correctly
      val mockControllerComponents = mock[ControllerComponents] //I am not sure if this is correct either
      val controller = new UserController(mockUserRepository,mockControllerComponents,mockSilhouette)

      val result:Future[Result] = controller.signupUser(FakeRequest())
      (result.map(response => {
        println("response: ",response)
        response mustBe BadRequest
      }))
    }
  }
}

Upvotes: 1

Views: 310

Answers (2)

halfer
halfer

Reputation: 20440

(Posted solution on behalf of the question author).

Here is the answer which worked. Thanks Mario.

class UserControllerUnitSpec extends PlaySpec /*with MockitoSugar*/ {

  "User signup request with non-JSON body" should {

    "return  400 (Bad Request) and the validation text 'Incorrect body type. Body type must be JSON'" in {

      val email = "[email protected]"
      val loginInfo = LoginInfo(CredentialsProvider.ID, email);
      val passwordInfo = PasswordInfo("someHasher","somePassword",Some("someSalt"))
      val internalUserProfile = InternalUserProfile(loginInfo,true,Some(passwordInfo))
      val externalUserProfile = ExternalUserProfile(email,"d","d",Some("somePassword"))
      val userProfile = UserProfile(Some(internalUserProfile),externalUserProfile)
      val user = User(UUID.randomUUID(),userProfile)

      println("testing with mocked User value",user);

      val mockUserRepository = mock(classOf[UsersRepository])
      // when(mockUserRepository.findUser(loginInfo)).thenReturn(Future(Some(user)))
     // when(mockUserRepository.saveUser(user)).thenReturn(Future(Some(user)))

     // val mockSilhouette = mock(classOf[Silhouette[JWTEnv]])
      val mockControllerComponents = Helpers.stubControllerComponents()//mock(classOf[ControllerComponents])
      /*
      The controller needs Silhouette. Using Silhouette's test kit to create fake instances.
      If you would like to test this controller, you must provide an environment that can handle your Identity and Authenticator implementation.
      For this case Silhouette provides a FakeEnvironment which automatically sets up all components needed to test your specific actions.

      You must only specify one or more LoginInfo -> Identity pairs that should be returned by calling request.identity in your action and
      the authenticator instance that tracks this user.
       */

      //User extends Identity trait
      /*
      Under the hood, the environment instantiates a FakeIdentityService which stores your given identities and returns it if needed.
      It instantiates also the appropriate AuthenticatorService based on your defined Authenticator type. All Authenticator services are real
      service instances set up with their default values and dependencies.
       */
      implicit val sys = ActorSystem("MyTest")
      implicit val mat = ActorMaterializer()
      implicit val env = 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 mockSilhouette = new SilhouetteProvider[JWTEnv](env,securedAction,unsecuredAction,userAware)

      val controller = new UserController(mockUserRepository,mockControllerComponents,mockSilhouette)

      val request = FakeRequest("POST","ws/users/signup")
      println("sending request",request)
      //val result = controller.someMethod()
      val result:Future[Result] = controller.signupUser(request)

      status(result) mustBe BAD_REQUEST

    }
  }
}

Upvotes: 1

Mario Galic
Mario Galic

Reputation: 48410

Regarding mockControllerComponents, Helpers.stubControllerComponents can be used instead of a mock:

val mockControllerComponents = Helpers.stubControllerComponents()

Regarding mockSilhouette, you have to setup the mock using when(...).thenReturn(...) similarly to how you have done it formockUserRepository, that is, inspect all the usages of silhouette inside signupUser and provide the appropriate method stubs:

val mockSilhouette = mock[Silhouette[JWTEnv]]
when(mockSilhouette.foo(...)).thenReturn(...)
when(mockUserRepository.bar(...)).thenReturn(...)
...

Upvotes: 1

Related Questions