mockitoscalatestplayframework-2.6silhouette

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 = "d@d.com"
      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
      }))
    }
  }
}

Solution

  • 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(...)
    ...