4

我正在尝试发明某种模拟 SecureSocial 动作生成器,或 SecureSocial 本身,以便能够对控制器方法进行单元测试。我找到了一些方法,例如使用 Securesocial 注释保护的单元测试方法使用依赖注入使用 SecureSocial 测试 Play2 应用程序,但问题是,在那个问题中,作者实际上不进行单元测试,而是进行集成测试.

我的单元测试如下所示:

  trait MockDaoProvider extends IDaoProvider {
    def entityDao = entityDaoMock
  }

  val controller = new MyController with MockDaoProvider

  "MyController.list" should {
    "return an OK" in {
      entityDaoMock.list().returns(List())

      val result = controller.list()(FakeRequest())

      status(result) must equalTo(OK)
    }
  }

如您所见,我模拟了依赖项以隔离和测试控制器方法实际执行的行为。

一切都还好,直到我将securesocial 中的SecuredAction 用于MyController.list 方法。现在我得到一个异常,测试失败。我不知道如何从 securesocial 模拟、存根或覆盖 SecuredAction 和 UserAwareAction 对象。我仍然不想将我的测试转换为 route(...) 测试。它们旨在仅测试控制器的行为。

有人遇到过同样的问题吗?可能有任何提示如何解决?

PS:Play framework 2.2.1,securesocial - 2.1.2

4

2 回答 2

3

代码的作者似乎真的没有强调可测试性,这迫使用户提出自己的新颖解决方案。用户jeantil的这个可能会有所帮助

class FakeAuthenticatorStore(app:Application) extends AuthenticatorStore(app) {
  var authenticator:Option[Authenticator] = None
  def save(authenticator: Authenticator): Either[Error, Unit] = {
    this.authenticator=Some(authenticator)
    Right()
  }
  def find(id: String): Either[Error, Option[Authenticator]] = {
    Some(authenticator.filter(_.id == id)).toRight(new Error("no such authenticator"))
  }
  def delete(id: String): Either[Error, Unit] = {
    this.authenticator=None
    Right()
  }
}

abstract class WithLoggedUser(val user:User,override val app: FakeApplication = FakeApplication()) extends WithApplication(app) with Mockito{
  lazy val mockUserService=mock[UserService]
  val identity=IdentityUser(Defaults.googleId, user)

  import helpers._
  import TestUsers._
  def cookie=Authenticator.create(identity) match {
    case Right(authenticator) => authenticator.toCookie
  }

  override def around[T: AsResult](t: =>T): execute.Result = super.around {
    mockUserService.find(Defaults.googleId) returns Some(identity)
    UserService.setService(mockUserService)
    t
  }
}

  val excludedPlugins=List(
    ,"service.login.MongoUserService"
    ,"securesocial.core.DefaultAuthenticatorStore"
  )
  val includedPlugins = List(
    "helpers.FakeAuthenticatorStore"
  )

  def minimalApp = FakeApplication(withGlobal =minimalGlobal, withoutPlugins=excludedPlugins,additionalPlugins = includedPlugins)

然后允许像这样进行测试

"create a new user password " in new WithLoggedUser(socialUser,minimalApp) {
  val controller = new TestController
  val req: Request[AnyContent] = FakeRequest().
    withHeaders((HeaderNames.CONTENT_TYPE, "application/x-www-form-urlencoded")).
    withCookies(cookie) // Fake cookie from the WithloggedUser trait

  val requestBody = Enumerator("password=foobarkix".getBytes) andThen Enumerator.eof
  val result = requestBody |>>> controller.create.apply(req)

  val actual: Int= status(result)
  actual must be equalTo 201
}
于 2014-01-26T17:17:12.693 回答
1

经过一番思考、探索和试验,我最终得到了一个优雅的解决方案。该解决方案依赖于依赖注入的“蛋糕模式”。像这样:

控制器中的代码:

trait AbstractSecurity {
  def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent]
}

trait SecureSocialSecurity extends AbstractSecurity with securesocial.core.SecureSocial {
   def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent] = SecuredAction { action }
}

abstract class MyController extends Controller with AbstractSecurity {

  def entityDao: IEntityDao

  def list = Secured { request =>
    Ok(
      JsArray(entityDao.list())
    )
  }
}

object MyController extends MyController with PsqlDaoProvider with SecureSocialSecurity

和测试代码:

 trait MockedSecurity extends AbstractSecurity {
    val user = Account(NotAssigned, IdentityId("test", "userpass"), "Test", "User",
      "Test user", Some("test@user.com"), AuthenticationMethod("userPassword"))

    def Secured(action: SecuredRequest[AnyContent] => play.api.mvc.Result): Action[AnyContent] = Action { request =>
      action(new SecuredRequest(user, request))
    }
  }


  val controller = new MyController with MockDaoProvider with MockedSecurity

  "IssueController.list" should {
    "return an OK" in {
      entityDaoMock.list().returns(List())

      val result = controller.list()(FakeRequest())

      status(result) must equalTo(OK)
    }
  }

仍然有一个缺点 - 测试也依赖于securesocial类......但是......这真的是一个缺点吗?我不知道这种方法在更复杂的情况下如何工作,我们拭目以待。

于 2014-01-27T06:35:28.317 回答