Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
700 views
in Technique[技术] by (71.8m points)

scala - Testing a Play2 application with SecureSocial using dependency injection

Thanks a lot for any guidance!

The SecureSocial plugin works fine when I run it from the browser, but I would like to be able to test the rest of my Play app now.

Quick Intro

SecureSocial's syntax looks like this:

def page = SecuredAction(WithProvider("google")) { ... }

or this:

def page = UserAwareAction { ... }

I've been looking here, which seems to be the only question on Stack Overflow even remotely related to my problem with SecureSocial, but I don't quite fancy rewiring bytecode. There should be a simpler solution to this.

When I'm running tests that access SecureSocial-protected Actions, I get a large error that I guess basically means I'm not passing it a user. (see the bottom of this question)

What I would like to do

Either inject all the functions to return a type Action instead of SecuredAction or UserAwareAction only during testing

Or actually pass in a test user to the call. But how?

What I have

@Singleton
class JsonOps @Inject() () extends Controller with SecureSocial {...}

A Global.scala written as described here and in my test...

val controller = new JsonOps
val result = controller.userAwareActionRequestForSomeJson("")(FakeRequest())

I also have calls like this:

// This is what I would use for production
def extjs = SecuredAction(WithProvider("google")) { implicit request =>
   Ok(views.html.extjs(request.user.firstName))
}
// This is what I would use for testing
def extjs = Action { implicit request =>
  Ok(views.html.extjs("testtesttesting"))
}

Which is why I think this problem might be well-suited for dependency injection? I'm not sure how I would do the class instantiation though, since the Global.scala I'm using is a generic class instantiator. I don't particularly wish to write 9000+ traits for each controller I have either.

The big error

This is line UserOpsSpec.scala line 12 and 13:

12  val controller = new UserOps
13  val result = controller.extjs()(FakeRequest())

and this is the error

[error]     RuntimeException: java.lang.ExceptionInInitializerError (UserOpsSpec.scala:13)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:220)
[error] securesocial.core.SecureSocial$.authenticatorFromRequest(SecureSocial.scala:200)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:81)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:78)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:215)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:51)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:51)
[error] play.api.Play$.current(Play.scala:51)
[error] securesocial.core.Authenticator$.cookieName$lzycompute(Authenticator.scala:188)
[error] securesocial.core.Authenticator$.cookieName(Authenticator.scala:188)
[error] securesocial.core.Authenticator$.<init>(Authenticator.scala:201)
[error] securesocial.core.Authenticator$.<clinit>(Authenticator.scala)
[error] securesocial.core.SecureSocial$.authenticatorFromRequest(SecureSocial.scala:200)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:81)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:78)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:215)
[error] null
[error] securesocial.core.SecureSocial$.authenticatorFromRequest(SecureSocial.scala:200)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:81)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:78)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:215)
[error] There is no started application
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:51)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:51)
[error] play.api.Play$.current(Play.scala:51)
[error] securesocial.core.Authenticator$.cookieName$lzycompute(Authenticator.scala:188)
[error] securesocial.core.Authenticator$.cookieName(Authenticator.scala:188)
[error] securesocial.core.Authenticator$.<init>(Authenticator.scala:201)
[error] securesocial.core.Authenticator$.<clinit>(Authenticator.scala)
[error] securesocial.core.SecureSocial$.authenticatorFromRequest(SecureSocial.scala:200)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:81)
[error] securesocial.core.SecureSocial$$anonfun$SecuredAction$1.apply(SecureSocial.scala:78)
[error] play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:215)
[info]
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's how I solved this problem. I am simulating a login (kind of) by directly adding to the Authenticator that's in memory. That returns a cookie which I include in the fake request. I'm doing this with an implicit conversion for some syntactic sugar. You can easily extend it for your particular case. My application only uses the user/pass provider, but you should be able to extend to use the other plugins.

 object TestUtils {

  @inline implicit def loggedInFakeRequestWrapper[T](x: FakeRequest[T]) = new LoggedInFakeRequest(x)

  final class LoggedInFakeRequest[T](val self: FakeRequest[T]) extends AnyVal {
    def withLoggedInUser(id: Long) = {
      val userToLogInAs:Identity = ??? //get this from your database using whatever you have in Global
      val cookie = Authenticator.create(userToLogInAs) match {
        case Right(authenticator) => authenticator.toCookie
      }
      self.withCookies(cookie)
    }
  }        
}

And the spec:

"render the index page" in {
      val home = route(FakeRequest(GET, "/").withLoggedInUser(1L)).get

      status(home) must equalTo(OK)
      //etc.
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...