Ren*_*Ren 5 post scala playframework specs2 playframework-2.0
我的控制器中有一个方法,我想直接打电话.它接受一个POSTed表单,验证它,然后返回一些东西.我想直接测试它 - 即不通过路径助手.
这是我的表单代码(FormFields只是一个案例类)
val searchForm = Form(
mapping(
"foo" -> nonEmptyText,
"filter" -> optional(text).verifying("Filter text must be 'published' or 'unpublished'",
x => x.isEmpty || x.get == "published" || x.get == "unpublished")
)(FormFields.apply)(FormFields.unapply)
Run Code Online (Sandbox Code Playgroud)
)
这是我的控制器电话.
def doThings() = IsAuthenticated {
username => implicit request => {
searchForm.bindFromRequest().fold(
formWithErrors => BadRequest(s"Incorrect data: ${formWithErrors.errors.map(x => s"${x.key} ${x.message}").mkString}."),
form => {
OK("Test text here")
}
)
}
Run Code Online (Sandbox Code Playgroud)
}
如果我通过我的路由文件调用此方法,如下所示 - 这可以按预期工作.表格被发布,验证,按预期返回OK("测试...").
即.以下工作(使用Specs2)
val request = FakeRequest(POST, "notarealurl")
.withFormUrlEncodedBody(
"filter" -> "published",
"foo" -> "Test"
).withSession("email" -> "testuser")
val Some(result) = route(request)
status(result) must equalTo(OK)
Run Code Online (Sandbox Code Playgroud)
但是,无论我试图直接调用该方法失败 - 失败发生在表单验证步骤.它告诉我,当我运行单元测试时,"foo"缺少一个值.这就是我试图这样做的方式.
val request = FakeRequest()
.withFormUrlEncodedBody(
"filter" -> "published",
"foo" -> "Test"
).withSession("email" -> "testuser")
//val Some(result) = route(request)
val result = Search.searchProducts()(request)
println(contentAsString(result))
status(result) must equalTo(OK)
Run Code Online (Sandbox Code Playgroud)
打印的文字是"搜索不正确:foo error.required".我想我没有正确地打电话,但我不知道我哪里出错了.
注意:此处的代码代表我的问题,但已被删除以说明问题.
我模仿你的逻辑,它运行良好.我用Play文档中的复制粘贴替换了一些代码,只是为了保持最小化.我在我正在进行的设置之上测试了它,所以你会看到默认播放设置的异物.此设置与我最初链接的文章中描述的设置或多或少相同.我不知道如何比这更直接:
在控制器中:
import play.api.data._
import play.api.data.Forms._
case class UserData(name: String, age: Int)
val userFormConstraints2 = Form(
mapping(
"name" -> nonEmptyText,
"age" -> number(min = 0, max = 100)
)(UserData.apply)(UserData.unapply)
)
def test = Action {
implicit request => {
userFormConstraints2.bindFromRequest().fold(
formWithErrors => BadRequest("bad"),
userData => {
Ok(userData.name + userData.age)
}
)
}
}
Run Code Online (Sandbox Code Playgroud)
测试:
class TempSpec extends Specification with MyHelpers {
"1" can {
"direct access to controller while posting" in new TestServer {
// `in new TestServer` spawns dependencies (`server`)
val controller = new controllers.Kalva(server)
// I instantiate the controller passing the dependency
val request = FakeRequest(POST, "bla")
.withFormUrlEncodedBody(
"name" -> "Richard",
"age" -> "1"
)
val result = controller.test()(request)
status(result) must equalTo(OK)
contentAsString(result) must contain("Richard");
val request_bad = FakeRequest(POST, "bla")
.withFormUrlEncodedBody(
"name" -> "",
"age" -> "-1"
)
val result_bad = controller.test()(request_bad)
status(result_bad) must equalTo(400)
contentAsString(result_bad) must contain("bad");
}
}
}
Run Code Online (Sandbox Code Playgroud)
Global.scala:
object Global extends GlobalSettings {
private lazy val injector = Guice.createInjector(new TestModule)
override def getControllerInstance[A](controller: Class[A]) = {
injector.getInstance(controller)
}
}
Run Code Online (Sandbox Code Playgroud)
TestModule:
import com.google.inject._
import com.tzavellas.sse.guice.ScalaModule
class TestModule extends ScalaModule {
def configure() {
@Provides
def getServer:Server = {
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
在routes文件中:
POST /bla @controllers.Kalva.test
// the `@` prefix is needed because of how we fetch controllers
Run Code Online (Sandbox Code Playgroud)
class TranslateSpec extends Specification {
"Translate" should {
// The normal Play! way
"accept a name, and return a proper greeting" in {
running(FakeApplication()) {
val translated = route(FakeRequest(GET, "/greet/Barney")).get
status(translated) must equalTo(OK)
contentType(translated) must beSome.which(_ == "text/html")
contentAsString(translated) must contain ("Barney")
}
}
// Providing a fake Global, to explitly mock out the injector
object FakeTranslatorGlobal extends play.api.GlobalSettings {
override def getControllerInstance[A](clazz: Class[A]) = {
new Translate(new FakeTranslator).asInstanceOf[A]
}
}
"accept a name, and return a proper greeting (explicitly mocking module)" in {
running(FakeApplication(withGlobal = Some(FakeTranslatorGlobal))) {
val home = route(FakeRequest(GET, "/greet/Barney")).get
contentAsString(home) must contain ("Hello Barney")
}
}
// Calling the controller directly, without creating a new FakeApplication
// (This is the fastest)
"accept a name, and return a proper greeting (controller directly, no FakeApplication!)" in {
val controller = new Translate(new FakeTranslator)
val result = controller.greet(name = "Barney")(FakeRequest())
contentAsString(result) must contain ("Hello Barney")
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码非常自我描述,它显示了默认的测试工作流以及如何使用依赖注入来改进它.这是本文的引用.
这个特别的摘录来自"我为什么要在游戏中使用DI?" 部分.这篇文章是关于使用Play2设置Google Guice以及它打开的可能性.这是一个实用的阅读.
正如你在上面所看到的那样,"正常的Play!方式"很好,但是通过接受DI,你可以在测试中获得更多(当然还有开发).
正如文章中所描述的,使用Guice with Play涉及对Play的默认设置进行微小更改,这非常值得.我已经这么做了很久了,并没有回头.