我正在对我的控制器类的这种方法进行单元测试。该方法执行很少的异步数据库查询,并根据结果Redirect
请求。上一个数据库查询的成功决定了是否需要执行下一个查询。
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
由 for 循环中的verifyUser
方法调用
为了测试代码,我编写了以下规范
"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)
...
}
上面的代码似乎是从我的规范中调用的 when(testEnv.controller.confirmSignupforUser(ArgumentMatchers.any[User])).thenReturn( 未来{ println(s"confirm mocked user ${testEnv.user}") 一些(testEnv.user) } )
我几乎没有怀疑。 问题1)我不是在嘲笑testEnv.controller
.我还能在testEnv.controller
(testEnv.controller.confirmSignupforUser(ArgumentMatchers.any[User])
)的方法之一中使用when
吗?
问题2)我是否正确,在for
循环中,userTokenRepo.findOne
应该返回模拟值Some(testEnv.userToken)
.然后应该使用它userRepo.findOne(tokenOption.get.userKeys)
它应该返回模拟值Some(testEnv.user)
。这应该在confirmSignupforUser(userOption.get)
中使用? 问题3)为什么confirmSignupforUser
会获得null
值?
我的坏。我意识到我不能使用when
并通过controller
的方法,因为我不是在嘲笑控制器。我删除了以下行,因为测试已经向前推进
//don't need this
when(testEnv.controller.confirmSignupforUser(ArgumentMatchers.any[User])).thenReturn(
Future{
println(s"confirming mocked user ${testEnv.user}")
Some(testEnv.user)
}
)