无法编写控制器方法的测试规范

时间:2019-03-27 17:23:01

标签: mockito scalatest playframework-2.6

我正在对控制器类的此方法进行单元测试。该方法执行少量异步数据库查询,并根据结果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“确认模拟用户$ {testEnv.user}”)           一些(testEnv.user)         }       )

我毫不怀疑。 问题1)我不是在嘲笑testEnv.controller。我还能在whentestEnv.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的值?

1 个答案:

答案 0 :(得分:0)

我的坏。我意识到我无法使用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)
    }
  )