Reputation: 16729
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
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
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