Fast jeder Softwareentwickler weiß gut geschriebene Unit- und Funktionstests zu schätzen, aber aus irgendeinem Grund schreiben die meisten von uns sie nicht gerne. Wie alles hat auch die testgetriebene Entwicklung ihre Vor- und Nachteile, aber selbst wenn man gegen TDD ist, sind Tests eine großartige Möglichkeit, die Ergebnisse von bereits durchgeführten und funktionierenden Funktionen zu sichern. Sie können verhindern, dass Entwickler ihre bisherige Arbeit bis zu einem gewissen Grad zerstören, während sie andere Funktionalitäten erstellen oder reparieren.

Dieser Artikel konzentriert sich auf das Schreiben von Tests für eine PlayFramework-Serveranwendung.a

ScalaTest

ScalaTest ist eine der meistgenutzten Scala-Testbibliotheken. Sie bietet die Integration mit populären Testprogrammen wie Mockito, JUnit, Selenium und mit populären IDEs wie IntelliJ, Eclipse, NetBeans. Die gesamte Bibliothek ist so konzipiert, dass sie so wenig wie möglich im Weg steht. Sie erlaubt es Entwicklern zu wählen, welcher Stil für das Schreiben von Tests am besten geeignet ist. Es gibt 8 verfügbare Codestile, die den meisten populären Bibliotheken wie xUnit, Ruby’s RSpec, specs2 ähneln.

Dank seiner Popularität hat ScalaTest eine Integrationsbibliothek für Play- scalatestplus-play . Um sie zu verwenden, müssen Sie nur ihre spezifische Version zu build.sbt wie folgt hinzufügen:

libraryDependencies ++= Seq(

„org.scalatestplus.play“ %% „scalatestplus-play“ % „x.x.x“ % „test“

)

 

Dabei steht x.x.x für eine bestimmte Version, die mit Ihrer Play-Version kompatibel ist.

Für das Testen von Play-Anwendungen wird empfohlen, den PlaySpec teststil zu verwenden, der Specs wie den Codestil verwendet.

Alle Testklassen und Hilfsklassen befinden sich im test dverzeichnis im Hauptkatalog des Projekts.

First test spec

Nehmen wir an, wir haben ein Calculation objekt, das wie folgt aussieht:

object Calculation {
   def add(x: Int, y: Int) = {
     x + y
   }
    def subtract(x: Int, y: Int) = {
     x - y
   }
}

Um dies zu testen, müssen wir eine neue Klasse erstellen, die PlaySpec erweitert.

class CalculationSpec extends PlaySpec {
   "The add method" must {
       "add two numbers" in {
       Calculation.add(2, 2) mustBe (4)
       }
   }
  
   "The subtract method" must {
       "subtract two numbers" in {
       Calculation.subtract(2, 2) mustBe (0)
       }
   }
}

Der obige Code testet unsere beiden Calculation Methoden. Wie Sie sehen können, ist diese Art, Tests zu schreiben, leicht zu lesen, da wir im Grunde Sätze bilden, die beschreiben, wie die Funktion funktionieren soll. Um die Wahrheit der gegebenen Aussage zu bestätigen, verwenden wir den mustBe Matcher.

Fügen wir eine weitere Methode zu unserem Calculation Objekt hinzu.

def divide(x: Int, y: Int) = {
   x / y
}

c

"The divide method" must {
   "divide given numbers" in {
       Calculation.divide(2, 2) mustBe (1)
   }
}

it must {
   "throw ArithmeticException when dividing by zero" in {
       assertThrows[ArithmeticException] {
       Calculation.divide(2, 0)
       }
   }
}

Der erste Name kommt mir bekannt vor, aber der andere ist ein bisschen anders. Zuerst verwendet it es, das den vorherigen Namen kopiert. Der nächste Unterschied liegt in der Behauptung. Da die Division durch 0 unmöglich ist, sollte unsere Methode eine Ausnahme auslösen, wenn dies geschieht. Um zu prüfen, ob sie ausgelöst wurde, verwenden wir assertThrows mit der Ausnahme, die wir abfangen wollen.

Mocking-Daten

Wie bereits erwähnt, unterstützt ScalaTest Data Mocking und Play verwendet Mockito für diese Aufgabe. Um Mocked-Daten in unserem Test zu verwenden, müssen wir eine Testspezifikationsklasse erstellen, die PlaySpec und MockitoSugar erweitert. Um zu zeigen, wie Mocking funktioniert, zeigt das folgende Beispiel zwei Tests. Einer mit einer gespotteten Funktion – sie gibt einen bestimmten Wert zurück, obwohl sie in ihrem aktuellen Zustand einen anderen Wert zurückgeben sollte – und der zweite ohne Mocking. Beide Tests sollten bestanden werden.

class TestClassSpec extends PlaySpec with MockitoSugar {
   "A mocked TestClass" must {
       "return a mocked value" in {
       val mockTest = mock[TestClass]
       when(mockTest.someMethod).thenReturn(10)
       mockTest.someMethod mustBe 10
       }
   }

   "A TestClass" must {
       "return a default value" in {
       val test = new TestClass
       test.someMethod mustBe 123
       }
   }
}
  
class TestClass {
   def someMethod = 123
}