我正在尝试发明某种模拟SecureSocial动作生成器,或者SecureSocial本身能够对控制器方法进行单元测试。 我找到了一些方法,比如Unit-testing methods secured with Securesocial annotation和Testing a Play2 application with SecureSocial using dependency injection但问题是,问题作者实际上并不进行单元测试,而是进行集成测试。
我的单元测试看起来像这样:
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 for MyController.list方法之前,一切都还可以。现在我得到一个例外,测试失败了。我不知道如何从securesocial模拟,存根或覆盖SecuredAction和UserAwareAction对象。我仍然不想将我的测试转换为路由(...)测试。它们仅用于测试控制器的行为。
有人遇到过同样的问题吗?可能有什么提示可以解决它吗?
PS:Play framework 2.2.1,securesocial - 2.1.2
答案 0 :(得分:3)
似乎代码的作者真的没有强调可测试性,这迫使用户提出他们自己的新颖解决方案。用户jeantil的This one可能会有所帮助:
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
}
答案 1 :(得分: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类......但是......它真的是一个缺点吗? 我不知道这种方法在更复杂的情况下是如何工作的,我们会看到。