50.8 Das Assertions-Modul: Sprechende Prüfausdrücke

Inspiriert von Chai.js haben wir eine eigene API für sprechende Asserts erstellt. Damit können Prüfausdrücke (Assertions) in einer formalisierten Sprache, die normale Wörter aus dem Englischen verwendet, formuliert werden. Sie kann in Groovy, JavaScript und Jython Skripten genutzt werden.

50.8.1 Motivation

Das Ziel ist, Prüfausdrücke, die in Skripten verwendet werden, besser lesbar zu machen und auch komplexe Datenstrukturen prüfen zu können. Aktuell geschieht die Überprüfung von Daten in Server und SUT Skripten im Normalfall über rc.check() oder über assert von Java. Das funktioniert gut für Basisdatentypen wie Zeichenketten. Es ist jedoch mühsam, komplexe Datenstrukturen wie strukturierte Objekte, die zum Beispiel aus einem JSON-String erzeugt wurden, damit zu prüfen. Die QF-Test Assertions API macht hier das Leben deutlich leichter.

Nachfolgend finden Sie zwei Groovy Skripte, die exemplarisch den Unterschied zwischen sprechenden Prüfausdrücken und rc.check() beziehungsweise assert() zeigen.

def foo = 'bar'
              def beverages = [ tea: [ 'chai', 'matcha', 'oolong' ] ]

              expect(foo).to.be.a('String')
              foo.should.be.equal('bar')
              expect(foo).to.have.lengthOf(3)
              expect(beverages).to.have.property('tea').with.lengthOf(3)
Beispiel 50.9:  Groovy-Skript mit sprechenden Asserts

 

def foo = 'bar'
              def beverages = [ tea: [ 'chai', 'matcha', 'oolong' ] ]

              rc.check(foo instanceof String,"")
              rc.checkEqual(foo,'bar',"")
              rc.checkEqual(foo.length(),3,"")
              assert(beverages.tea!=null)
              assert(beverages.tea.size()==3)
Beispiel 50.10:  Datenprüfung mit QF-Test rc.check und Java assert

50.8.2 API-Dokumentation

Die QF-Test Assertions API bietet die Schnittstellen Assert und expect. In Groovy-Skripten steht auch zusätzlich should zur Verfügung. expect und should unterstützen Wort- bzw. Aufrufketten. Die Syntax von Assert ist eher die klassische Assert-Syntax wie in Java.

Das Prüfergebnis wird als erfolgreicher bzw. fehlgeschlagener Check oeder optional als Exception im Protokoll gespeichert. Zusätzlich kann das Ergebnis auch als boolescher Wert abgefragt und in einer Variable geschrieben werden. Details hierzu finden Sie in Ergebnisbehandlung.

Die API-Dokumentation steht in doc/javadoc/qfaa.zip zur Verfügung. Darin sind alle verfügbaren Methoden für Assert enthalten. Diese können ebenfalls bei expect oder should (in Groovy) als Teil der Wortketten verwendet werden. Da die QF-Test Assertions API sehr ähnlich zu Chai.js ist, funktionieren die meisten Beispiele von https://www.chaijs.com/api/ auch in QF-Test. Die Methoden, die in Chai.js verfügbar sind, aber noch nicht für QF-Test implementiert wurden, sind in "Nicht verfügbare Prüfausdrücke" aufgelistet.

Für Assert kann eine Autovervollständigung mit Dokumentation der verfügbaren Methoden aktivieren werden, indem Sie Assert. eintippen und anschließend Strg⁠+⁠Leertaste gleichzeitig drücken.

Reguläre Ausdrücke - Regexps können auch genutzt werden. Diese sind über das Modul java.util.regex.Pattern implementiert.

Hinweis Eine Einführung in die Erweiterung der Assertions API mit eigenen Assertions finden Sie in unserem Blogbeitrag Die QF-Test Assertion API erweitern – Eine praktische Einführung.

50.8.2.1 Wortketten

Der größte Vorteil ist die gute Lesbarkeit, die über Wortketten erreicht wird. Diese können bei expect() und should() verwendet werden. Die folgenden Ausdrücke können aneinander gekettet werden: .to .be .been .is .that .which .and .has .have .with .at .of .same .but .does .still .also

def testObj = [
              "name": "test",
              "sub": [
              "name": 'test sub'
              ],
              "numbers": [1, 2, 3, 4],
              "hasNumbers" : true
              ];

              expect(testObj).to.be.an('Object').and.is.ok
              expect(testObj).to.have.property('sub').that.is.an('Object').and.is.ok
              expect(testObj.sub).to.have.property('name').that.is.a('String').and.to.equal('test sub')
              expect(testObj).to.have.property('numbers').that.deep.equals([1, 2, 3, 4])
              expect(testObj).to.have.property('hasNumbers', true)
Beispiel 50.11:  Wortketten bei expect

 

rc.setLocal("jsonData", """
              {
              "Actors": [
              {
              "name": "Tom Cruise",
              "age": 56,
              "Born At": "Syracuse, NY",
              "Birthdate": "July 3, 1962",
              "photo": "https://jsonformatter.org/img/tom-cruise.jpg",
              "wife": null,
              "weight": 67.5,
              "hasChildren": true,
              "hasGreyHair": false,
              "children": [
              "Suri",
              "Isabella Jane",
              "Connor"
              ]
              },
              {
              "name": "Robert Downey Jr.",
              "age": 53,
              "Born At": "New York City, NY",
              "Birthdate": "April 4, 1965",
              "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg",
              "wife": "Susan Downey",
              "weight": 77.1,
              "hasChildren": true,
              "hasGreyHair": false,
              "children": [
              "Indio Falconer",
              "Avri Roel",
              "Exton Elias"
              ]
              }
              ]
              }""")

              def data = rc.getJson("jsonData")
              data.Actors.should.be.a("ArrayList")
              expect(data.Actors[0]).to.be.a("LinkedHashMap")
              Assert.instanceOf(data.Actors[0], "LinkedHashMap", "Bla")
              data.Actors[0].name.should.be.a("String")
              data.Actors[0].age.should.be.a("Long")
              data.Actors[0].weight.should.be.a("Double")
              data.Actors[0].hasChildren.should.be.a("Boolean")
              rc.setGlobalJson("gData",data)
Beispiel 50.12:  Wortketten bei should

Die Dokumentation hierzu finden sie in https://www.chaijs.com/api/bdd.

50.8.2.2 Unterschiede zwischen dem QF-Test Assertions API und Chai.js

Durch die Implementierung in Java ergeben sich ein paar Unterschiede zu Chai.js.

50.8.2.3 Nicht verfügbare Prüfausdrücke

Einige der Prüfungen, die unter Chai.JS zu verfügung stehen, können nicht direkt von Javascript nach Java übertragen werden, und einige Assertions sind noch nicht implementiert. Dazu gehören unter anderem:

Assert

Expect/Should

50.8.3 Ergebnisbehandlung

Ergebnisbehandlung mit Assert, expect() und should() (System)
Server (automatisch weiter an SUT) Skript-Name: OPT_PLAY_HANDLE_ASSERTION
Mögliche Werte: VAL_PLAY_HANDLE_ASSERTION_AS_CHECK, VAL_PLAY_HANDLE_ASSERTION_WITH_EXCEPTION, VAL_PLAY_HANDLE_ASSERTION_SILENTLY

 

Mit dieser Option beeinflusst man den Rückgabewert und die Protokollierung.

  • “Als Check behandeln” - VAL_PLAY_HANDLE_ASSERTION_AS_CHECK
    Im Fehlerfall wird ein Fehler protokolliert, im Erfolgsfall ein erfolgreicher Check.
  • “Als Exception” - VAL_PLAY_HANDLE_ASSERTION_WITH_EXCEPTION
    Es wird im Fehlerfall eine Exception geworfen. Sie kann in Skripten als Throwable und in Try-Catch-Knoten als ScriptException gefangen werden
  • “Als Rückgabewert” - VAL_PLAY_HANDLE_ASSERTION_SILENTLY
    Die Prüfung wird ausgeführt, aber nicht als Fehler gekennzeichnet. Es wird auch keine Exception geworfen. Das Ergebnis der Prüfung wird als Wert zurückgegeben, entweder true oder false. Wenn expect oder should verwendet wird, muss .getResult() angehängt werden, um an den Rückgabewert zu gelangen.
  • VAL_PLAY_HANDLE_ASSERTION_AUTOMATICALLY (Standardwert)
    Entspricht VAL_PLAY_HANDLE_ASSERTION_AS_CHECK, außer bei der Verwendung der QF-Test Assertions API in einem Unit-Test-Knoten. Dann wird automatisch VAL_PLAY_HANDLE_ASSERTION_WITH_EXCEPTION verwendet um kompatibel mit JUnit zu sein.

Die Option wird am Anfang des Skripts gesetzt:

rc.setOption(Options.OPT_PLAY_HANDLE_ASSERTION, Options.VAL_PLAY_HANDLE_ASSERTION_SILENTLY)
              def a = 54
              def b = 55
              isEqual = Assert.test(a==b, "")
              if (isEqual) {
              ...
              }
Beispiel 50.14:  Prüfung mit Rückgabewert in einem Groovy-Skript

Wenn die Assertion in natürlicher Sprache formuliert wird, muss .getResult() angehängt werden, um an den Rückgabewert zu gelangen:

rc.setOption(Options.OPT_PLAY_HANDLE_ASSERTION, Options.VAL_PLAY_HANDLE_ASSERTION_SILENTLY)
              a = 54
              b = 55
              isEqual = expect(a).to.equal(b).getResult()
              if isEqual.getResult():
              ...
Beispiel 50.15:  Prüfung mit Rückgabewert in einem Jython-Skript