如何在play framework 2 scala中对控制器进行单元测试

The*_*rav 7 unit-testing scala playframework-2.0

假设我有一个控制器,其动作可以接收两个参数.

它调用两个服务,每个参数一个,服务都返回字符串

每个字符串都作为参数传递给模板

结果传递给Ok并返回.

我想编写一个简单的单元测试来确保:1 - 使用正确的参数调用正确的服务2 - 来自服务的返回值被传递给模板的正确属性

最好的方法是什么?

Mik*_*378 3

使用 Mockito 和 Specs2,我模拟服务来验证它们的方法调用。

我的控制器是由 Spring 实例化的。这让我可以将它视为 aclass而不是object。=> 这对于使其可controller测试至关重要。这里有一个例子:

@Controller
class MyController @Autowired()(val myServices: MyServices) extends Controller
Run Code Online (Sandbox Code Playgroud)

要为控制器启用 Spring,您必须定义一个Global对象,例如 Play! 文档解释:

object Global extends GlobalSettings {

  val context = new ClassPathXmlApplicationContext("application-context.xml")

  override def getControllerInstance[A](controllerClass: Class[A]): A = {
    context.getBean(controllerClass)
  }
}
Run Code Online (Sandbox Code Playgroud)

我的单元测试不需要Spring;我只是将协作者(模拟)传递给构造函数。

但是,对于呈现的模板,我仅测试结果的类型(Ok、BadRequest、重定向等...)。事实上,我注意到让我的测试详细扫描整个渲染模板(发送给它的参数等)并不容易,仅进行单元测试。

因此,为了断言使用正确的参数调用正确的模板,我相信运行 Selenium 的验收测试或可能的功能测试(如果您愿意)可以扫描整个预期结果。

2 - 服务的返回值被传递到模板的正确属性

检查这一点非常容易..如何?通过信任编译器!更喜欢将一些自定义类型传递给模板,而不是简单的基元,例如: phone: String 会变成:phone: Phone。(一个简单的值对象)。因此,不必担心以非预期顺序将属性传递给模板(在单元测试或实际生产代码中)。编译器确实会警告。

这是我使用specs2进行单元测试(简化)的一个示例:(您会注意到包装器的使用:)WithFreshMocks。这case class将允许在测试后刷新所有变量(在本例中为模拟)测试。因此这是重置模拟的好方法。

    class MyControllerSpec extends Specification with Mockito {

      def is =
        "listAllCars should retrieve all cars" ! WithFreshMocks().listAllCarsShouldRetrieveAllCars

      case class WithFreshMocks() {

        val myServicesMock = mock[MyServices]
        val myController = new MyController(myServicesMock)

        def listAllCarsShouldRetrieveAllCars = {
          val FakeGetRequest = FakeRequest() //fakeRequest needed by controller
          mockListAllCarsAsReturningSomeCars()
          val result = myController.listAllCars(FakeGetRequest).asInstanceOf[PlainResult] //passing fakeRequest to simulate a true request
          assertOkResult(result).
            and(there was one(myServicesMock).listAllCars()) //verify that there is one and only one call of listAllCars. If listAllCars would take any parameters that you expected to be called, you could have precise them.
        }

        private def mockListAllCarsAsReturningSomeCars() { 
           myServicesMock.listAllCars() returns List[Cars](Car("ferrari"), Car("porsche"))
        }

        private def assertOkResult(result: PlainResult) = result.header.status must_== 200

       }
Run Code Online (Sandbox Code Playgroud)