はじめに
scalaの認証テストsilhoetteを使った場合のリクエストテストの方法。
routeに対して認証をかけた時、普通にリクエストテストを実行すると落ちるが、それを回避するためのtips。
内容は殆ど公式準拠。
Test Helpers
playを拡張するヘルパーライブラリをまずinstall。
libraryDependencies ++= Seq(
"com.mohiva" %% "play-silhouette-testkit" % "version" % "test"
)
テストのファイルにimport。
import com.mohiva.play.silhouette.test._
併用するテストフレームワークは選ばないが、以下の例はすべてSpecs2。
Test silhouette actions
以下のようなコントローラをテストしたい。
その場合、IdentityとAuthenticatorをハンドルするFaceEnvironmentを使用してテストを実行する。
class UserController(silhouette: Silhouette[MyEnv]) extends Controller {
/**
* userを取得.
* 認証あり.
*/
def user = silhouette.SecuredAction { implicit request =>
// 認証が通っている場合はok
Ok(Json.toJson(request.identity))
}
/**
* userが認証されているかチェック.
* コントローラ自体に認証はかかっていないがどのユーザからのアクセスかは知覚する.
*/
def isAuthenticated = silhouette.UserAwareAction { implicit request =>
request.identity match {
case Some(identity) => Ok // 認証済みuserからのアクセスはok
case None => Unauthorized // 認証されていないuserからのアクセスはUnauthorized
}
}
}
FakeEnvironment
テスト用のFakeEnvironmentにはLoginInfoとIdentityのペアを渡しさえすれば良い。
あとは勝手にFakeIdentityServicerやAuthenticatorServiceをいい感じに作成してくれる。
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com"))
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
FakeRequest
silhouetteのテストヘルパーはplay組み込みテストヘルパーのFakeRequestを拡張する。
1つ目のメソッドはAuthenticatorを受け取りリクエスト内に組み込む。
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com"))
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val authenticator = new CookieAuthenticator("test", identity.loginInfo, ...)
val request = FakeRequest().withAuthenticator(authenticator)
2つ目のメソッドはAuthenticatorの元になるLoginInfoを受け取り、リクエストに組み込む。
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com"))
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest().withAuthenticator(identity.loginInfo)
NOTE
これらのメソッドを使用する際にはimplicitなenvironmentを同じスコープにおいておく必要がある。
実際のテスト
authenticator(認証子)の欠落ケースのテスト
リクエスト内にtokenなどの認証子がないケースのテストでは、リクエストに何も渡さずにテストすればいい。
class UserSpec extends PlaySpecification {
"The `user` method" should {
"authenticatorがない場合401が返る" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com"))
val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest() // リクエストに何も渡さない
val controller = app.injector.instanceOf[UserController]
val result = controller.user(request)
status(result) must equalTo(UNAUTHORIZED)
}
}
"The `isAuthenticated` method" should {
"authenticatorがない場合401が返る" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com"))
val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest() // リクエストに何も渡さない
val controller = app.injector.instanceOf[UserController]
val result = controller.isAuthenticated(request)
status(result) must equalTo(UNAUTHORIZED)
}
}
}
Identity(認証対象)が存在しないケースのテスト
authenticatorに対応するidentityがないケースをテストする場合、user(ここで言うIdentity)とauthenticatorに異なるlogin infoを渡せばいい。
class UserSpec extends PlaySpecification {
"The `user` method" should {
"return status 401 if authenticator but no identity was found" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com")) // ここのlogin infoと、
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest()
.withAuthenticator(LoginInfo("xing", "comedian@watchmen.com")) // ここのlogin infoが違う
val controller = app.injector.instanceOf[UserController]
val result = controller.user(request)
status(result) must equalTo(UNAUTHORIZED)
}
}
"The `isAuthenticated` method" should {
"return status 401 if authenticator but no identity was found" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com")) // ここのlogin infoと、
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest()
.withAuthenticator(LoginInfo("xing", "comedian@watchmen.com")) // ここのlogin infoが異なる
val controller = app.injector.instanceOf[UserController]
val result = controller.isAuthenticated(request)
status(result) must equalTo(UNAUTHORIZED)
}
}
}
Identityの正常認証ケースのテスト
正常な認証ケースをテストしたい場合同じlogin infoをidentityとauthenticatorに渡せばいい。
class UserSpec extends PlaySpecification {
"The `user` method" should {
"return status 200 if authenticator and identity was found" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com")) // ここのlogin infoと、
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest().withAuthenticator(identity.loginInfo) // ここのlogin infoが同じ
val controller = app.injector.instanceOf[UserController]
val result = controller.user(request)
status(result) must equalTo(OK)
}
}
"The `isAuthenticated` method" should {
"return status 200 if authenticator and identity was found" in new WithApplication {
val identity = User(LoginInfo("facebook", "apollonia.vanova@watchmen.com")) // ここのlogin infoと、
implicit val env = FakeEnvironment[MyEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest().withAuthenticator(identity.loginInfo) // ここのlogin infoが同じ
val controller = app.injector.instanceOf[UserController]
val result = controller.isAuthenticated(request)
status(result) must equalTo(OK)
}
}
}