scala play框架如何单元测试异步控制器

时间:2017-06-12 22:34:27

标签: scala unit-testing asynchronous playframework

使用Scala播放版本2.5并尝试按照以下文档中的单元测试控制器指南进行操作:https://www.playframework.com/documentation/2.5.x/ScalaTestingWithScalaTest

没有单元测试异步控制器的例子。

我正在尝试为我的控制器创建一个单元测试,它有一个异步操作方法,我最后嘲笑了一些对象

class ProductController @Inject()(
    action: ProductAction,
    handler: ProductResourceHandler)(implicit ec: ExecutionContext)
    extends Controller {

  /**
   * Fetch a list of products
   */
  def index: Action[AnyContent] = {
    action.async { implicit request =>
      handler.find.map { list =>                                
        Ok(Json.toJson(list))
      }
    }
  }
// ...
}

我的单元测试:

import scala.concurrent.Future
import org.scalatestplus.play._
import play.api.mvc._
import play.api.test._
import play.api.test.Helpers._
import org.scalatest.mockito.MockitoSugar
import product.ProductAction
import product.ProductController
import product.services.maps.GeolocationService
import product.ProductResourceHandler
import play.api.libs.concurrent.Execution.Implicits._
import scala.io.Source
import play.api.libs.json.Json
import product.model.OfferList
import product.model.OfferDetail
import org.mockito.Mockito._

class ProductControllerSpec extends PlaySpec with Results with MockitoSugar {

  private val productList = Json.parse(Source.fromFile("conf/app/sample_products.json").getLines.mkString).as[ProductList]

  "Example Page#index" should {
    "should be valid" in {
      val action = new ProductAction()
      val handler = mock[ProductResourceHandler]      
      when(handler.find) thenReturn Future.successful(productList)      
      val controller = new ProductController(action, handler)
      val result: Future[Result] = controller.index().apply(FakeRequest())      
      val bodyText: String = contentAsString(result)                                
      bodyText != null mustBe true
    }
  }
}

到目前为止它正在运行,但我想知道这是否遵循此类测试的最佳实践或指南。这是在Scala play框架中对异步控制器进行单元测试的正确方法吗?

2 个答案:

答案 0 :(得分:1)

我的一些建议是

  • 使用contentAsJson代替contentAsString并检查返回的json。
  • 使用route直接调用控制器并测试响应(例如route(app, FakeRequest..
  • 使用status方法检查返回的状态是否为HTTP OK(状态码200)

    val Some(result) = route(app, FakeRequest(GET, 
                              controllers.routes. ProductController.index.path()))
    status(result) must be (OK)
    val json = contentAsJson(result)
    // inspect json fields like if you have to check if the json 
    // has string field called id you can do (json \ "id").as[String] must be ("<id value>")
    

答案 1 :(得分:1)

根据Play documentation

  

默认情况下,播放操作是异步的。

这意味着,即使您没有使用Action.async { Future { myAnonymousFunction } }而只使用Action { myAnonymousFunction }internallymyAnonymousFunction的结果也会包含在Future中。

例如,说你有

class HelloWorld extends Controller {
  def index = Action { request => Ok("") }
}

然后

(new HelloWorld).index().apply(FakeRequest())

仍有类型

Future[Result]

这让我相信你的单元测试确实是测试控制器的合适方式,也就是说,Play的文档也隐含地涵盖了Action.async的情况。