Я тестирую этот метод моего класса контроллеров. Метод выполняет несколько асинхронных запросов к базе данных и, в зависимости от результата, Redirect
s запрос. Успешность предыдущего запроса к базе данных определяет, должен ли быть выполнен следующий запрос.
def verifyUser(token:String) = Action.async {
implicit request => {
println("verifyUser action called with token: " + token) //TODOM - add proper handling and response
val result:Future[Result] = for{tokenOption:Option[UserToken] <- userTokenRepo.findOne(UserTokenKey(UUID.fromString(token))) //generator 1 - get token from database
userOption:Option[User] <- if (tokenOption.isDefined) {println(s"received tokenOption ${tokenOption}");userRepo.findOne(tokenOption.get.userKeys)} else {Future.successful(None)} //generator2. found token, look for corresponding user to which the token belongs
modifiedUser:Option[User] <- if (userOption.isDefined) {println(s"received userOption ${userOption}");confirmSignupforUser(userOption.get)} else Future.successful(None) //generator 3. found user and token. Update profile
deletedToken:Option[UserTokenKey] <- if(modifiedUser.isDefined) {println(s"received modified ${modifiedUser}");userTokenRepo.delete(UserTokenKey(UUID.fromString(token)))} else Future.successful(None)
}
yield { //check if we have user and token and modified user here. If any is missing, return error else success
println("db query results tokenOption: "+tokenOption+", userOption: "+userOption+" : modifiedUserOption: "+modifiedUser+", deletedToken: "+deletedToken)
if(tokenOption.isDefined && userOption.isDefined && modifiedUser.isDefined && deletedToken.isDefined)
Redirect("http://localhost:9000/home"+";signup=success")//TODOM - pick from config
else
if(tokenOption.isEmpty)
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config
else if(userOption.isEmpty)
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config
else if(modifiedUser.isEmpty)
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config
else //this shouldn't happen. Unexpected
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config
}
result.recover { case x => {
println("Future failed in validateUserSession. Recovering. Returning Internal Server Error" + x)
}
}
result //returning Future[Result]
}
}
Контроллер также имеет метод confirmSignupforUser
, который вызывается методом verifyUser
в цикле for
Чтобы проверить код, я написал следующую спецификацию
"verify token method" should {
"work " in {
val testEnv = new TestEnv(components.configuration)
when(testEnv.mockUserTokenRepository.findOne(ArgumentMatchers.any[UserTokenKey])).thenReturn(
Future{
println(s"returning mocked token ${testEnv.userToken}")
Some(testEnv.userToken)}
)
when(testEnv.mockUserRepository.findOne(ArgumentMatchers.any[UserKeys])).thenReturn(Future{
println(s"returning mocked user ${testEnv.user}")
Some(testEnv.user)
})
when(testEnv.controller.confirmSignupforUser(ArgumentMatchers.any[User])).thenReturn(
Future{
println(s"confirming mocked user ${testEnv.user}")
Some(testEnv.user)
}
)
when(testEnv.mockUserTokenRepository.delete(ArgumentMatchers.any[UserTokenKey])).thenReturn(
Future{
println(s"returning mocked token key ${testEnv.userTokenKey}")
Some(testEnv.userTokenKey)
}
)
val request = FakeRequest("POST", s"ws/users/signup/${testEnv.mockHelperMethods.getUniqueID()}")
println("sending request", request)
val resultFuture:Future[Result] = testEnv.controller.verifyUser(testEnv.mockHelperMethods.getUniqueID().toString()).apply(request)
val responseBody = contentAsString(resultFuture)
println(s"received response ${responseBody}")
1 mustBe 1
}
}
Мой тест вызывает исключение null
указателя.
created TestEnv with configuration...
confirming user: null
returning mocked user User(11111111-1111-1111-1111-111111111111,UserProfile(Some(InternalUserProfile(LoginInfo(credentials,test@test.com),1,true,Some(PasswordInfo(someHasher,somePassword,Some(someSalt))))),ExternalUserProfile(test@test.com,ln,fn,Some(somePassword))))
returning mocked token UserToken(11111111-1111-1111-1111-111111111111,11111111-1111-1111-1111-111111111111,UserKeys(1,test@test.com,LoginInfo(credentials,test@test.com),fn,ln),2019-03-27T17:08:43.861Z,true)
java.lang.NullPointerException was thrown.
java.lang.NullPointerException
at controllers.UserController.confirmSignupforUser(UserController.scala:442)
Кусок кода, который, похоже, вызывает проблему,
def confirmSignupforUser(user:User):Future[Option[User]] = {
println("confirming user: "+user)
...
}
Код выше вызывается из моей спецификации, кажется
когда (testEnv.controller.confirmSignupforUser (ArgumentMatchers.any [Пользователь])). thenReturn (
Будущее{
println (s "подтверждение поддельного пользователя $ {testEnv.user}")
Некоторые из них (testEnv.user)
}
)
У меня мало сомнений.
вопрос 1) я не издеваюсь testEnv.controller
. Могу ли я по-прежнему использовать when
в одном из методов testEnv.controller
(testEnv.controller.confirmSignupforUser(ArgumentMatchers.any[User])
)?
Вопрос 2) Правильно ли, что в цикле for
, userTokenRepo.findOne
должно возвращать ложное значение Some(testEnv.userToken)
. Затем следует использовать userRepo.findOne(tokenOption.get.userKeys)
, который должен возвращать смоделированное значение Some(testEnv.user)
. Это следует использовать в confirmSignupforUser(userOption.get)
?
Вопрос 3) Почему confirmSignupforUser
получает null
значение?