Play Framework에서 컨트롤러가 뷰에 전달하는 인수에 대해 테스트를 실행하는 방법

StackOverflow https://stackoverflow.com//questions/20005032

  •  20-12-2019
  •  | 
  •  

문제

우리의 플레이 애플리케이션에서 모든 컨트롤러 기능은 데이터베이스(또는 다른 방법)에서 데이터를 가져오고 이 값을 결과에 전달합니다.

def index = Action { implicit request =>
    val newsItems: List[String] = fetchNewsFromDB()
    Ok(views.html.home.index(newsItems))
  }

def fetchNewsFromDB() = List("Headline1", "Headline2")

사양을 사용하여 테스트를 작성하고 있습니다(문서를 기반으로 함). http://www.playframework.com/documentation/2.2.x/ScalaTest)

이 문서에 따르면 컨트롤러는 다음과 같습니다.다음 테스트에서는 색인 페이지에 헤드라인이 포함되어 있는지 확인하고 싶습니다."headline" 클래스가 있는 div가 있는지 확인하여 이 작업을 수행합니다.

"Example Page#index" should {
    "should contain a headline" in {
      val controller = new TestController()
      val result: Future[SimpleResult] = controller.index().apply(FakeRequest())
      val bodyText: String = contentAsString(result)
      bodyText.toLowerCase must contain("<div class=\"headline\"")
    }
  }

그러나 컨트롤러가 뷰에 전달하는 뉴스 항목 목록이 비어 있지 않은지 확인하고 싶습니다.

이를 수행하는 가장 좋은 방법은 무엇입니까?컨트롤러를 거의 수정하지 않아도 되는 일반적인 방식으로 이것이 가능합니까?

도움이 되었습니까?

해결책

나 역시 템플릿으로 가는 도중 매개변수를 가로챌 수 없어 좌절감을 느꼈다. 실제로 템플릿을 렌더링하는 것조차 극도로 어려워질 수 있었다. 조금도 페이지에 "상태"가 많은 경우 테스트합니다(예: 사용자 개체, 탐색 도우미 등을 제공하는 암시적 항목).

결국 내가 한 일은 컨트롤러의 테스트 가능성을 위해 추가 "심"을 추가하는 것이었습니다.내 테스트에서 나는 연장하다 HTML 렌더링 기능을 모의 기능으로 대체한 다음 매개변수를 확인하는 데 사용할 수 있습니다.

다음은 "뉴스" 작업을 기반으로 한 간단한 예입니다.첫째, 더 이상 컨트롤러가 아닌 컨트롤러입니다. object 그래서 우리는 그것을 확장할 수 있습니다:

object Application extends ApplicationController

trait ApplicationController extends Controller {

  def newsAction = Action {
    Ok(renderNews("this is the news"))
  }

  def renderNews(s:List[String]):Html = html.sandbox(s)
}

그만큼 renderNews 방법은 우리에게 가장 중요한 "테스트 솔기"를 제공합니다.나는 이것이 실제로 컨트롤러 메소드의 가독성도 향상시킨다고 생각합니다. 그것은 좋은 일입니다 :-)

이제 단위 테스트는 다음과 같습니다.

class ApplicationSpec extends Specification with Mockito {

  val mockedNewsRenderer = mock[List[String] => Html]

  val controller = new ApplicationController {
    override def renderNews(s:List[String]) = mockedNewsRenderer(s)
  }

  "Application News Controller" should {

    "Pass a non-empty list of news items to the template" in {
      val result = controller.newsAction(FakeRequest())

      status(result) must beEqualTo(200)

      val captor = ArgumentCaptor.forClass(classOf[List[String]])

      there was one(mockedNewsRenderer).apply(captor.capture())
      val theArgument = captor.getValue
      theArgument.isEmpty must beFalse
    }
  }
}

우리는 renderNews 함수를 대체할 수 있도록 컨트롤러를 확장한 다음(물론 다른 내용은 변경하지 않음) 정상적으로 액션을 호출합니다.우리는 여전히 표준 Play를 얻습니다. Result 상태 코드 등을 계속 확인할 수 있지만 그런 다음 다음을 사용할 수 있습니다. Mockito는 Specs2에 내장된 기능을 확인합니다., 함께 모키토의 ArgumentCaptor 시설 템플릿이 실제로 호출되었고 비어 있지 않은 문자열 목록이 제공되었는지 확인합니다.

이 접근 방식은 저에게 효과적이었습니다. 빠르게 실행되고 작성하기 쉬운 단위 테스트를 통해 컨트롤러의 코드 적용 범위가 정말 좋아졌습니다.

다른 팁

You have a very good question and a very valid point on testing controllers, but I'm afraid it can't be done easily. The problem is that the views compile to Scala functions meaning when you call views.html.home.index(newsItems) it will return an object of Html, which already has the Html put together and compiled. If you would like to test what get's passed in you need to intercept it before the view is called.

To solve this you would have to rewrite your controllers, by moving all your business logic out of the controller and only have the necessary request handling code there. That would almost be easier to test.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top