Как выполнить тесты по аргументу, который контроллер передает в view in 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")

Я пишу тесты, используя specifiactions (на основе документации http://www.playframework.com/documentation/2.2.x/ScalaTest)

В соответствии с этой документацией контроллером выполняется следующее.В следующем тесте я хочу убедиться, что индексная страница содержит заголовок.Я делаю это, проверяя, существует ли div с классом "headline"

"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\"")
    }
  }

Однако я бы предпочел проверить, является ли список NewsItems, который контроллер передает в представление, непустым.

Как лучше всего это сделать?Возможно ли это сделать общим способом, для которого требуется небольшая модификация контроллеров?

Это было полезно?

Решение

Я тоже был разочарован тем, что не смог перехватить параметры на их пути к шаблону - и на самом деле может стать чрезвычайно сложно даже заставить шаблон отображаться вообще в тестах, если у вас много "состояний" на ваших страницах (например, имплицитные элементы, которые предоставляют объект пользователя, навигационные помощники и т.д.).

В итоге я вставил дополнительный "шов" для тестируемости в мои контроллеры;в своих тестах я расширять тестируемый контроллер заменяет функцию рендеринга 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 функцию, расширяем контроллер так, чтобы мы могли заменить его (обратите внимание, что мы, конечно, больше ничего в нем не меняем), а затем вызываем действие как обычно.Обратите внимание, что мы по-прежнему получаем стандартную игру 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