So führen Sie Tests für das Argument aus, das ein Controller an die Ansicht im Play-Framework übergibt

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

  •  20-12-2019
  •  | 
  •  

Frage

In unserer Play-Anwendung ruft jede Controller-Funktion Daten aus der Datenbank (oder auf andere Weise) ab und übergibt diese Werte an das Ergebnis

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

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

Ich schreibe Tests mit Spezifikationen (basierend auf der Dokumentation http://www.playframework.com/documentation/2.2.x/ScalaTest)

Gemäß dieser Dokumentation durch den Controller wie folgt.Im nächsten Test möchte ich sicherstellen, dass die Indexseite eine Überschrift enthält.Ich mache das, indem ich überprüfe, ob es ein Div mit der Klasse "Überschrift" gibt

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

Ich würde jedoch lieber prüfen, ob die Liste newsItems, die der Controller an die Ansicht übergibt, nicht leer ist.

Was ist der beste Weg, dies zu tun?Ist dies auf generische Weise möglich, für die eine geringe Modifikation der Steuerungen erforderlich ist?

War es hilfreich?

Lösung

Auch ich war frustriert, dass ich die Parameter auf ihrem Weg zur Vorlage nicht abfangen konnte - und tatsächlich kann es extrem schwierig werden, die Vorlage überhaupt zum Rendern zu bringen überhaupt in Tests, wenn Sie viele "Status" auf Ihren Seiten haben (z. B. Implizite, die das Benutzerobjekt, Navigationshilfen usw. bereitstellen).

Am Ende habe ich eine zusätzliche "Naht" für die Testbarkeit in meine Controller eingefügt;in meinen Tests habe ich erweitern der zu testende Controller ersetzt die HTML-Rendering-Funktion durch eine verspottete, mit der ich dann die Parameter überprüfen kann.

Hier ist ein einfaches Beispiel, das auf Ihrer Aktion "Nachrichten" basiert;erstens der Controller, der kein Controller mehr ist object damit wir es erweitern können:

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

Der renderNews methode gibt uns die alles entscheidende "Testnaht".Ich denke, es verbessert auch die Lesbarkeit von Controller-Methoden, was schön ist :-)

Nun der Komponententest:

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

Wir erstellen einen Mock, um für die zu stehen renderNews funktion, erweitern Sie den Controller so, dass wir ihn ersetzen können (beachten Sie, dass wir natürlich nichts anderes daran ändern), und rufen Sie dann die Aktion wie gewohnt auf.Beachten Sie, dass wir immer noch ein Standardspiel bekommen Result so können wir immer noch Statuscodes usw. überprüfen, aber dann können wir die verwenden Mockito verifiziert die in Specs2 integrierte Funktionalität, zusammen mit Mockitos ArgumentCaptor Einrichtung um zu behaupten, dass unsere Vorlage tatsächlich aufgerufen wurde und dass sie mit einer nicht leeren Liste von Zeichenfolgen geliefert wurde.

Dieser Ansatz hat bei mir gut funktioniert - er ermöglicht es, mit schnell laufenden und einfach zu schreibenden Komponententests eine wirklich gute Codeabdeckung Ihrer Controller zu erzielen.

Andere Tipps

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top