Como executar os testes no argumento de que um controlador passa para o modo de exibição no Play Framework
-
20-12-2019 - |
Pergunta
Na nossa maneira de jogar aplicação de cada controlador de função obtém dados a partir do banco de dados (ou de alguma outra forma) e passa esses valores para o resultado
def index = Action { implicit request =>
val newsItems: List[String] = fetchNewsFromDB()
Ok(views.html.home.index(newsItems))
}
def fetchNewsFromDB() = List("Headline1", "Headline2")
Eu estou escrevendo testes usando specifiactions (com base na documentação http://www.playframework.com/documentation/2.2.x/ScalaTest)
De acordo com esta documentação pelo controlador da seguinte forma.No próximo teste, eu quero certificar-se de que a página de índice contém um cabeçalho.Eu faço isso verificando se existe uma div com a classe "manchete"
"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\"")
}
}
No entanto, eu gostaria de, ao invés de verificar se a lista newsItems que o controlador passa para o modo de exibição não é vazio.
Qual é a melhor maneira de fazer isso?É possível fazer isso de uma forma genérica para que pouco a modificação dos controladores é necessário?
Solução
Eu também fiquei frustrado que eu não conseguia interceptar os parâmetros em seu caminho para o modelo - e, na verdade, pode tornar-se extremamente difícil até mesmo para obter o modelo para compor em todos os nos testes, se você tem um monte de "estado" em suas páginas (por exemplo, implicits que fornecer o objeto de usuário, navegação ajudantes, etc.).
O que eu acabei fazendo foi a colocação de uma "costura" para testabilidade na minha controladores;em meus testes, eu ampliar o controlador em teste, substituição de renderização HTML função com um escarnecido, que eu possa usar para verificar os parâmetros.
Aqui está um exemplo simples baseado em suas "notícias" Ação;primeiro, o controlador, que não é mais um object
assim, podemos estendê-lo:
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)
}
O renderNews
método nos dá o "teste da costura".Eu acho que ele realmente melhora a legibilidade do controlador de métodos muito, o que é bom :-)
Agora, o teste de unidade:
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
}
}
}
Criamos um mock para stand-in para o renderNews
função de estender o controlador de modo que podemos substituir em (note que nós não altere mais nada sobre ele, claro) e, em seguida, chamar a ação como normal.Observe que temos ainda um padrão de Jogo Result
assim, podemos ainda verificar códigos de status etc, mas a gente pode usar o Mockito verificar a funcionalidade que é construído para Specs2, juntamente com Mockito do ArgumentCaptor
instalação para afirmar que o nosso modelo foi, de fato, chamados, e que foi fornecido com um não-vazia lista de cadeias de caracteres.
Esta abordagem tem funcionado bem para mim - ele torna possível para ficar realmente bom de cobertura de código do seu controladores de rápida execução e de fácil escrever testes de unidade.
Outras dicas
Você tem uma pergunta muito boa e um ponto muito válido, em testes de controladores, mas estou com medo de que ele não pode ser feito facilmente.O problema é que os pontos de vista de compilação para Scala funções significado quando você chamar views.html.home.index(newsItems)
ele vai retornar um objeto de Html
, que já tem o Html juntos e compilado.Se você gostaria de testar o que se passou em que você precisa para interceptá-lo antes que a exibição é chamado.
Para resolver isso, você teria que reescrever seus controladores, movendo toda a sua lógica de negócios do controlador e tem somente o necessário solicitar o código de manipulação de lá.Que seria quase mais fácil de testar.