djs*_*dog 10 testing scala specs2 playframework-2.0
我发现了以下问题/答案:
在Play 2.0 FakeRequest中测试MultipartFormData
但似乎Play 2.1中的情况发生了变化.我试过调整这样的例子:
"Application" should {
"Upload Photo" in {
running(FakeApplication()) {
val data = new MultipartFormData(Map(), List(
FilePart("qqfile", "message", Some("Content-Type: multipart/form-data"),
TemporaryFile(getClass().getResource("/test/photos/DSC03024.JPG").getFile()))
), List())
val Some(result) = routeAndCall(FakeRequest(POST, "/admin/photo/upload", FakeHeaders(), data))
status(result) must equalTo(CREATED)
headers(result) must contain(LOCATION)
contentType(result) must beSome("application/json")
Run Code Online (Sandbox Code Playgroud)
但是每当我尝试运行请求时,我都会得到一个空指针异常:
[error] ! Upload Photo
[error] NullPointerException: null (PhotoManagementSpec.scala:25)
[error] test.PhotoManagementSpec$$anonfun$1$$anonfun$apply$3$$anonfun$apply$4.apply(PhotoManagementSpec.scala:28)
[error] test.PhotoManagementSpec$$anonfun$1$$anonfun$apply$3$$anonfun$apply$4.apply(PhotoManagementSpec.scala:25)
[error] play.api.test.Helpers$.running(Helpers.scala:40)
[error] test.PhotoManagementSpec$$anonfun$1$$anonfun$apply$3.apply(PhotoManagementSpec.scala:25)
[error] test.PhotoManagementSpec$$anonfun$1$$anonfun$apply$3.apply(PhotoManagementSpec.scala:25)
Run Code Online (Sandbox Code Playgroud)
如果我尝试用刚刚路由替换已弃用的routeAndCall(并删除结果周围的Option),我会收到一个编译错误,指出它无法将一个MultipartFormData [TemporaryFile]实例写入HTTP响应.
使用Scala在Play 2.1中设计此测试的正确方法是什么?
编辑:尝试修改代码以仅测试控制器:
"Application" should {
"Upload Photo" in {
val data = new MultipartFormData(Map(), List(
FilePart("qqfile", "message", Some("Content-Type: multipart/form-data"),
TemporaryFile(getClass().getResource("/test/photos/DSC03024.JPG").getFile()))
), List())
val result = controllers.Photo.upload()(FakeRequest(POST, "/admin/photo/upload",FakeHeaders(),data))
status(result) must equalTo(OK)
contentType(result) must beSome("text/html")
charset(result) must beSome("utf-8")
contentAsString(result) must contain("Hello Bob")
}
Run Code Online (Sandbox Code Playgroud)
但是我现在在结果的所有测试条件上得到类型错误,如下所示:
[error] found : play.api.libs.iteratee.Iteratee[Array[Byte],play.api.mvc.Result]
[error] required: play.api.mvc.Result
Run Code Online (Sandbox Code Playgroud)
我不明白为什么我得到一个映射到结果的字节数组的Interator.这可能与我如何使用自定义身体解析器有关吗?我的控制器的定义如下:
def upload = Action(CustomParsers.multipartFormDataAsBytes) { request =>
request.body.file("qqfile").map { upload =>
Run Code Online (Sandbox Code Playgroud)
使用此帖子中的表单解析器:从Play2/Scala内存中的MultipartFormData中提取文件
Ale*_*rju 14
Play 2.3包含较新版本的httpmime.jar,需要进行一些小的修正.在使用Play的可写机制的Marcus解决方案的基础上,同时保留了我的Play 2.1解决方案中的一些语法糖,这就是我提出的:
import scala.language.implicitConversions
import java.io.{ByteArrayOutputStream, File}
import org.apache.http.entity.ContentType
import org.apache.http.entity.mime.MultipartEntityBuilder
import org.apache.http.entity.mime.content._
import org.specs2.mutable.Specification
import play.api.http._
import play.api.libs.Files.TemporaryFile
import play.api.mvc.MultipartFormData.FilePart
import play.api.mvc.{Codec, MultipartFormData}
import play.api.test.Helpers._
import play.api.test.{FakeApplication, FakeRequest}
trait FakeMultipartUpload {
implicit def writeableOf_multiPartFormData(implicit codec: Codec): Writeable[MultipartFormData[TemporaryFile]] = {
val builder = MultipartEntityBuilder.create().setBoundary("12345678")
def transform(multipart: MultipartFormData[TemporaryFile]): Array[Byte] = {
multipart.dataParts.foreach { part =>
part._2.foreach { p2 =>
builder.addPart(part._1, new StringBody(p2, ContentType.create("text/plain", "UTF-8")))
}
}
multipart.files.foreach { file =>
val part = new FileBody(file.ref.file, ContentType.create(file.contentType.getOrElse("application/octet-stream")), file.filename)
builder.addPart(file.key, part)
}
val outputStream = new ByteArrayOutputStream
builder.build.writeTo(outputStream)
outputStream.toByteArray
}
new Writeable[MultipartFormData[TemporaryFile]](transform, Some(builder.build.getContentType.getValue))
}
/** shortcut for generating a MultipartFormData with one file part which more fields can be added to */
def fileUpload(key: String, file: File, contentType: String): MultipartFormData[TemporaryFile] = {
MultipartFormData(
dataParts = Map(),
files = Seq(FilePart[TemporaryFile](key, file.getName, Some(contentType), TemporaryFile(file))),
badParts = Seq(),
missingFileParts = Seq())
}
/** shortcut for a request body containing a single file attachment */
case class WrappedFakeRequest[A](fr: FakeRequest[A]) {
def withFileUpload(key: String, file: File, contentType: String) = {
fr.withBody(fileUpload(key, file, contentType))
}
}
implicit def toWrappedFakeRequest[A](fr: FakeRequest[A]) = WrappedFakeRequest(fr)
}
class MyTest extends Specification with FakeMultipartUpload {
"uploading" should {
"be easier than this" in {
running(FakeApplication()) {
val uploadFile = new File("/tmp/file.txt")
val req = FakeRequest(POST, "/upload/path").
withFileUpload("image", uploadFile, "image/gif")
val response = route(req).get
status(response) must equalTo(OK)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*rju 13
我根据各种邮件列表建议设法使用Play 2.1.我是这样做的:
import scala.language.implicitConversions
import java.io.{ ByteArrayOutputStream, File }
import org.apache.http.entity.mime.MultipartEntity
import org.apache.http.entity.mime.content.{ ContentBody, FileBody }
import org.specs2.mutable.Specification
import play.api.http.Writeable
import play.api.test.{ FakeApplication, FakeRequest }
import play.api.test.Helpers._
trait FakeMultipartUpload {
case class WrappedFakeRequest[A](fr: FakeRequest[A]) {
def withMultipart(parts: (String, ContentBody)*) = {
// create a multipart form
val entity = new MultipartEntity()
parts.foreach { part =>
entity.addPart(part._1, part._2)
}
// serialize the form
val outputStream = new ByteArrayOutputStream
entity.writeTo(outputStream)
val bytes = outputStream.toByteArray
// inject the form into our request
val headerContentType = entity.getContentType.getValue
fr.withBody(bytes).withHeaders(CONTENT_TYPE -> headerContentType)
}
def withFileUpload(fileParam: String, file: File, contentType: String) = {
withMultipart(fileParam -> new FileBody(file, contentType))
}
}
implicit def toWrappedFakeRequest[A](fr: FakeRequest[A]) = WrappedFakeRequest(fr)
// override Play's equivalent Writeable so that the content-type header from the FakeRequest is used instead of application/octet-stream
implicit val wBytes: Writeable[Array[Byte]] = Writeable(identity, None)
}
class MyTest extends Specification with FakeMultipartUpload {
"uploading" should {
"be easier than this" in {
running(FakeApplication()) {
val uploadFile = new File("/tmp/file.txt")
val req = FakeRequest(POST, "/upload/path").
withFileUpload("image", uploadFile, "image/gif")
val response = route(req).get
status(response) must equalTo(OK)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经修改了Alex的代码以充当Writable,它更好地集成到Play 2.2.2中
package test
import play.api.http._
import play.api.mvc.MultipartFormData.FilePart
import play.api.libs.iteratee._
import play.api.libs.Files.TemporaryFile
import play.api.mvc.{Codec, MultipartFormData }
import java.io.{FileInputStream, ByteArrayOutputStream}
import org.apache.commons.io.IOUtils
import org.apache.http.entity.mime.MultipartEntity
import org.apache.http.entity.mime.content._
object MultipartWriteable {
/**
* `Writeable` for multipart/form-data.
*
*/
implicit def writeableOf_multiPartFormData(implicit codec: Codec): Writeable[MultipartFormData[TemporaryFile]] = {
val entity = new MultipartEntity()
def transform(multipart: MultipartFormData[TemporaryFile]):Array[Byte] = {
multipart.dataParts.foreach { part =>
part._2.foreach { p2 =>
entity.addPart(part._1, new StringBody(p2))
}
}
multipart.files.foreach { file =>
val part = new FileBody(file.ref.file, file.filename, file.contentType.getOrElse("application/octet-stream"), null)
entity.addPart(file.key, part)
}
val outputStream = new ByteArrayOutputStream
entity.writeTo(outputStream)
val bytes = outputStream.toByteArray
outputStream.close
bytes
}
new Writeable[MultipartFormData[TemporaryFile]](transform, Some(entity.getContentType.getValue))
}
}
Run Code Online (Sandbox Code Playgroud)
这样可以写出这样的东西:
val filePart:MultipartFormData.FilePart[TemporaryFile] = MultipartFormData.FilePart(...)
val fileParts:Seq[MultipartFormData.FilePart[TemporaryFile]] = Seq(filePart)
val dataParts:Map[String, Seq[String]] = ...
val multipart = new MultipartFormData[TemporaryFile](dataParts, fileParts, List(), List())
val request = FakeRequest(POST, "/url", FakeHeaders(), multipart)
var result = route(request).get
Run Code Online (Sandbox Code Playgroud)