如何在控制器中执行单元测试?

Posted

技术标签:

【中文标题】如何在控制器中执行单元测试?【英文标题】:How to perform unit test in controller? 【发布时间】:2015-12-29 10:12:51 【问题描述】:

假设,我有如下控制器:

class JenisKejahatanControl @Inject()(service: JenisKejahatanService, val messagesApi: MessagesApi) extends Controller with I18nSupport 

  def add = Action.async  implicit request =>
    lazy val incoming = JenisKejahatan.formJenisK.bindFromRequest()

    incoming.fold( error => 
      lazy val response = ErrorResponse(BAD_REQUEST, messagesApi("request.error"))
      Future.successful(BadRequest(Json.toJson(response)))
    ,  newJenisK =>
      lazy val future = service.addJenisK(newJenisK)
      future.flatMap 
        case Some(jenis) => Future.successful(Created(Json.toJson(SuccessResponse(jenis))))
        case None => Future.successful(BadRequest(Json.toJson(ErrorResponse(NOT_FOUND, messagesApi("add.jenis.kejahatan.fail")))))
      
    )
  

我想使用 specs2 测试我的 def add,怎么做?

【问题讨论】:

playframework.com/documentation/2.4.x/… 我已经阅读了它,但我仍然不明白如何将它应用于我的控制器 你想测试什么?这种方法的逻辑?路线?你能用语言描述你想测试什么吗? (这里可以写很多测试) 对不起,我是单元测试的初学者。我想测试 add 方法的路由,例如:当我运行 route /api/add/jenis 时,它会将数据 jenis kejahatan 添加到数据库中,而不必使用邮递员?对不起,如果我的英语这么差 【参考方案1】:

由于您的控制器已注入组件,因此我认为您缺少的一点是如何在满足各种依赖关系的规范中获取它的实例。为此,您可以使用 GuiceApplicationBuilder 获取 Play 应用程序实例,然后使用其 injector 获取控制器的实例,而无需手动构建它(更多依赖注入文档 here,特别是关于使用 Guice 进行测试here.)

如果您可以像示例中那样手动构建控制器,那很好,并且使事情变得更简单,但是控制器往往具有非平凡的依赖关系,您很可能希望使用 @987654328 上的 overrides 方法来模拟这些依赖关系@。

通过构造控制器的实例,将模拟(假)请求“应用”到您的操作方法并确定它们提供您期望的状态和主体非常简单。这是一个例子:

import controllers.SuccessResponse, JenisKejahatanControl
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import play.api.mvc.Result
import play.api.test.FakeRequest, PlaySpecification

import scala.concurrent.Future

class JenisKejahatanControlSpec extends PlaySpecification 

  "JenisKejahatanControl#add" should 
    "be valid" in 

      // Build an instance of a Play application using the
      // default environment and configuration, and use it
      // to obtain an instance of your controller with the
      // various components injected.
      val jenisController = new GuiceApplicationBuilder()
        .overrides(  // Mock the data service
          bind[JenisKejahatanService]
           .toInstance(new MockJenisKejahatanService))
        .build()
        .injector
        .instanceOf[JenisKejahatanControl]

      // Create a "fake" request instance with the appropriate body data
      val request = FakeRequest().withFormUrlEncodedBody("name" -> "test")

      // Apply the request to your action to obtain a response
      val eventualResult: Future[Result] = jenisController.add.apply(request)

      // Check the status of the response
      status(eventualResult) must_== CREATED

      // Ensure the content of the response is as-expected
      contentAsJson(eventualResult).validate[SuccessResponse].asOpt must beSome.which  r =>
        r.jenis.name must_== "test"
      
    
  

【讨论】:

哇,谢谢伙计,但我还是不明白如何将 GuiceApplicationBuilder 用于组件 JenisKejahatanService 和 MessageApi? @kurokoA2 因为 JenisKejahatanService 和 MessagesApi 都是控制器的 注入依赖项,所以 GuiceApplicationBuilder 应该使用我发布的代码来处理它们。如果不了解更多关于您的应用程序的信息,我就无法深入了解您在问题中发布的内容(这确实是一个不同的问题。)请务必阅读文档中的testing with Guice。 @kurokoA2 我已经扩展了示例,其中数据服务被测试模拟覆盖。您不必担心 MessagesApi - 应用程序构建器将使用默认的。 当我尝试制作构建实例时,我得到错误找不到类型 MockJenisKejahatanService,如何定义 mockJenisKejahatanService?

以上是关于如何在控制器中执行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XUnit 对 Web API 控制器进行单元测试

在 AngularJS 单元测试中模拟 $modal

在单元测试中以编程方式执行 Unwind Segue

第 6 章 unittest 单元测试框架 - Selenium3 自动化测试

软件测试之如何编写单元测试用例

如何运行单元测试项目并将结果返回给外部应用程序?