Comment exécuter des tests sur l'argument qu'un contrôleur transmet à la vue dans Play Framework

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

  •  20-12-2019
  •  | 
  •  

Question

Dans notre application de jeu, chaque fonction du contrôleur récupère les données de la base de données (ou d'une autre manière) et transmet ces valeurs au résultat.

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

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

J'écris des tests en utilisant des spécifications (basées sur la documentation http://www.playframework.com/documentation/2.2.x/ScalaTest)

Selon cette documentation par le contrôleur comme suit.Lors du prochain test, je veux m'assurer que la page d'index contient un titre.Je fais cela en vérifiant s'il existe un div avec la classe "titre"

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

Cependant, je préfère vérifier si la liste newsItems que le contrôleur transmet à la vue n'est pas vide.

Quelle est la meilleure façon de procéder?Est-il possible de le faire de manière générique pour laquelle peu de modifications des contrôleurs sont nécessaires ?

Était-ce utile?

La solution

Moi aussi, j'étais frustré de ne pas pouvoir intercepter les paramètres lors de leur chemin vers le modèle - et en fait, il peut devenir extrêmement difficile même d'obtenir le rendu du modèle. du tout dans les tests si vous avez beaucoup "d'état" dans vos pages (par exemple, des implicites qui fournissent l'objet utilisateur, des aides à la navigation, etc.).

Ce que j'ai fini par faire, c'est d'ajouter une « couture » supplémentaire pour la testabilité de mes contrôleurs ;lors de mes tests, je étendre le contrôleur testé, en remplaçant la fonction de rendu HTML par une fonction simulée, que je peux ensuite utiliser pour vérifier les paramètres.

Voici un exemple simple basé sur votre action « actualités » ;d'abord, le contrôleur, qui n'est plus un object afin que nous puissions l'étendre :

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

Le renderNews La méthode nous donne la « couture de test » très importante.Je pense que cela améliore également la lisibilité des méthodes du contrôleur, ce qui est bien :-)

Maintenant, le test unitaire :

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
    }
  }
}

Nous créons une simulation pour remplacer le renderNews fonction, étendez le contrôleur afin que nous puissions le remplacer (notez que nous ne changeons rien d'autre à ce sujet bien sûr), puis appelez l'action comme d'habitude.Notez que nous obtenons toujours un Play standard Result nous pouvons donc toujours vérifier les codes d'état, etc., mais nous pouvons ensuite utiliser le Mockito vérifie la fonctionnalité intégrée à Specs2, ensemble avec Mockito ArgumentCaptor facilité pour affirmer que notre modèle a bien été appelé et qu'il a été fourni avec une liste de chaînes non vide.

Cette approche a bien fonctionné pour moi : elle permet d'obtenir une très bonne couverture du code de vos contrôleurs avec des tests unitaires rapides et faciles à écrire.

Autres conseils

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top