Java >> Java Tutorial >  >> Java

Unit-Tests mit Spock Framework schreiben:Einführung in die Spezifikationen, Teil Zwei

Der vorherige Teil dieses Tutorials hat die Struktur einer Spock-Spezifikation beschrieben und uns geholfen, unsere erste Spezifikation zu schreiben.

Obwohl es wichtig ist, die Grundlagen zu verstehen, sind unsere Spezifikationen nicht sehr nützlich, da wir nicht wissen, wie wir das erwartete Verhalten des Systems unter Spezifikation beschreiben können.

Dieser Blogbeitrag behebt dieses Problem. Beginnen wir mit einem Blick auf die Struktur einer Feature-Methode.

Die Struktur einer Feature-Methode

Eine Merkmalsmethode beschreibt das erwartete Verhalten eines Merkmals, das von dem zu spezifizierenden System implementiert wird. Wir können unsere Feature-Methoden benennen, indem wir String verwenden Literale, und das ist ein großer Vorteil gegenüber JUnit (oder TestNG), weil wir Sätze verwenden können, die tatsächlich Sinn machen.

Der Quellcode einer einfachen Spezifikation, die eine Feature-Methode hat, sieht wie folgt aus:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {
    }
}

Jede Merkmalsmethode besteht aus sogenannten Blöcken. Jeder Block hat eine Bezeichnung, und der Hauptteil des Blocks erstreckt sich bis zum Anfang des nächsten Blocks oder bis zum Ende der Feature-Methode. Eine Feature-Methode kann die folgenden Blöcke haben:

  • Die Einrichtung block muss der erste Block einer Feature-Methode sein und enthält die Konfiguration des beschriebenen Features. Eine Feature-Methode kann nur 1 setup haben blockieren.
  • Das wann und dann Block beschreibt den Stimulus (wann ) und die erwartete Antwort (dann ). Eine Feature-Methode kann mehrere wann und dann haben Blöcke.
  • Die erwarten block beschreibt den Stimulus und die erwartete Reaktion in einem einzigen Ausdruck. Eine Feature-Methode kann nur einen expect haben Block, und es ist möglich, sowohl wann als auch dann hinzuzufügen und erwarten Blöcke in die gleichen Feature-Methoden. Dies ist jedoch nicht sehr praktisch.
  • Die Aufräumarbeiten block wird verwendet, um die von einer Feature-Methode verwendeten Ressourcen zu bereinigen, und wird selbst dann aufgerufen, wenn die Feature-Methode eine Ausnahme auslöst. Eine Feature-Methode kann nur eine Bereinigung haben blockieren.
  • Das wo block muss der letzte Block einer Feature-Methode sein und wird verwendet, um datengesteuerte Feature-Methoden zu schreiben. Eine Feature-Methode kann nur ein where haben blockieren.

Die folgende Abbildung veranschaulicht die Struktur einer Feature-Methode:

Mit anderen Worten, die Struktur einer Feature-Methode sieht wie folgt aus:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {
		//setup block
		//when and then blocks
		//expect block
		//cleanup block
		//where block
    }
}

Fahren wir fort und schreiben unsere ersten Feature-Methoden.

Funktionsmethoden schreiben

Wir können Feature-Methoden schreiben, indem wir die im vorherigen Abschnitt erwähnten Blöcke verwenden. Beginnen wir damit, herauszufinden, wie wir das Setup verwenden können blockieren.

Verwendung des Setup-Blocks

Wie wir bereits wissen, ist das setup Block enthält den Einrichtungscode der beschriebenen Funktion. Wir können ein Setup erstellen mit dem Label:setup blockieren und Hinzufügen des Einrichtungscodes nach diesem Etikett. Zum Beispiel, wenn wir eine HashMap erstellen wollen -Objekt und fügen Sie einen Wert in die erstellte Karte ein, unser setup Block sieht wie folgt aus:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {

        setup:
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)
    }
}

Allerdings ist die Einrichtung Etikett ist optional. Wenn wir uns entscheiden, es wegzulassen, erstellen wir ein implizites setup Block, der wie folgt aussieht:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {

        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)
    }
}

Wenn wir unsere Feature-Methoden mit dem gegebenen-wenn-dann-Format schreiben möchten, können wir das setup ersetzen Label mit dem Label:gegeben und beschreiben Sie unser Setup blockieren, indem Sie einen String verwenden wörtlich. Eine Einrichtung Block, der diesen Ansatz verwendet, sieht wie folgt aus:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)
    }
}

Nachdem wir das beschriebene Feature konfiguriert haben, müssen wir sein Verhalten spezifizieren. Lassen Sie uns herausfinden, wie wir es tun können, indem wir wann und dann verwenden Blöcke.

Wenn- und Dann-Blöcke verwenden

Wir können das Verhalten der beschriebenen Funktion spezifizieren, indem wir wann und dann verwenden Blöcke, die immer zusammen auftreten. Das wann Block beschreibt den Reiz und das dann Block beschreibt die erwartete Antwort.

Wir können ein wann erstellen blockieren, indem Sie diese Regeln befolgen:

  • Ein wann Block muss mit dem Label beginnen:when .
  • Ein wann Block kann eine zusätzliche Beschreibung haben, die mit einem String angegeben wird wörtlich.
  • Ein wann Block kann beliebigen Code enthalten.

Wir können ein dann erstellen blockieren, indem Sie diese Regeln befolgen:

  • Ein dann -Block muss direkt nach dem when platziert werden Block, der den Stimulus beschreibt.
  • Ein dann Block muss mit dem Label beginnen:then .
  • Ein dann Block kann eine zusätzliche Beschreibung haben, die mit einem String angegeben wird wörtlich.
  • Ein dann Block kann nur Variablendefinitionen, Bedingungen, Ausnahmebedingungen und Interaktionen enthalten.

Wenn wir überprüfen möchten, ob unsere Karte den richtigen Wert zurückgibt, wenn ein Wert mit dem angegebenen Schlüssel gefunden wird, müssen wir das folgende wann und dann hinzufügen block in unsere Feature-Methode:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)

        when: 'A value is found with the given key'
        def found = map.get(key)

        then: 'Should return the found value'
        found == value
    }
}

Wir können auch mehrere wann und dann hinzufügen Blöcke in eine Feature-Methode. Wenn wir beispielsweise überprüfen möchten, ob unsere Karte in jeder Situation den richtigen Wert zurückgibt (ein Wert wird gefunden und ein Wert wird nicht gefunden), müssen wir das folgende wann und dann hinzufügen Blöcke in unsere Feature-Methode:

import spock.lang.Specification

class MapSpec extends Specification {

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def incorrectKey = 'incorrectKey'
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)

        when: 'A value is found with the given key'
        def found = map.get(key)

        then: 'Should return the found value'
        found == value

        when: 'A value is not found with the given key'
        found = map.get(incorrectKey)

        then: 'Should return null'
        found == null
    }
}

Wenn wir uns die in diesem Abschnitt beschriebenen Feature-Methoden genauer ansehen, stellen wir fest, dass das when Block wirkt etwas künstlich. Lassen Sie uns weitermachen und herausfinden, wie wir dieselbe Feature-Methode schreiben können, indem wir expect verwenden blockieren.

Verwendung des Expect-Blocks

Eine Erwartung Block beschreibt den Stimulus und die erwartete Reaktion in einem einzigen Ausdruck. Wir können einen expect erstellen blockieren, indem Sie diese Regeln befolgen:

  • Eine Erwartung Block muss mit dem Label beginnen:expect .
  • Eine Erwartung Block kann eine zusätzliche Beschreibung haben, die mit einem String angegeben wird wörtlich.
  • Eine Erwartung Block kann nur Bedingungen und Variablendefinitionen enthalten.

Lassen Sie uns unsere Feature-Methode umschreiben, die überprüft, ob unsere Karte den richtigen Wert zurückgibt, wenn ein Wert mit dem angegebenen Schlüssel gefunden wird. Wir können dies tun, indem wir das wann und dann ersetzen Block mit dem folgenden expect blockieren:

import spock.lang.Specification

class ExpectSpec extends Specification {

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)

        expect: 'Should return the found value when a value is found with the given key'
        map.get(key) == value
    }
}

Unsere Arbeit ist jedoch noch nicht getan. Wir müssen noch überprüfen, ob unsere Karte den richtigen Wert zurückgibt, wenn ein Wert mit dem angegebenen Schlüssel nicht gefunden wird. Wir können dies auf saubere Weise tun, indem wir die und verwenden Etikett. Die und label wird verwendet, um einzelne Teile eines Blocks zu beschreiben, und es hat eine optionale Beschreibung, die durch die Verwendung eines String angegeben wird wörtlich.

Wir können unsere Feature-Methode abschließen, indem wir ihr die folgenden Zeilen hinzufügen:

import spock.lang.Specification

class ExpectSpec extends Specification {

    def 'Get value from a map'() {

        given: 'Map contains one key-value pair'
        def incorrectKey = 'incorrectKey'
        def key = 'key'
        def value = 1

        def map = new HashMap()
        map.put(key, value)

        expect: 'Should return the found value when a value is found with the given key'
        map.get(key) == value

        and: 'Should return null when a value is not found with the given key'
        map.get(incorrectKey) == null
    }
}

Wir wissen jetzt, wie wir das getestete Feature beschreiben können, indem wir sowohl wann als auch dann verwenden und erwarten Blöcke. Das ist ein guter Anfang, aber manchmal reserviert unsere Feature-Methode Ressourcen, die anschließend freigegeben werden müssen. Lassen Sie uns herausfinden, wie wir diese Ressourcen mit der Bereinigung bereinigen können blockieren.

Verwendung des Cleanup-Blocks

Eine Aufräumaktion block wird verwendet, um alle Ressourcen freizugeben, die von einer Feature-Methode verwendet werden, und Spock garantiert, dass es aufgerufen wird, selbst wenn die Feature-Methode eine Ausnahme auslöst. Wir können eine Bereinigung erstellen blockieren, indem Sie diese Regeln befolgen:

  • Eine Aufräumaktion Block muss mit dem Label beginnen:cleanup .
  • Eine Aufräumaktion -Block muss nach dem wann und dann platziert werden und/oder erwarten Blöcke.

Nehmen wir an, wir haben eine Feature-Methode, die eine neue Datei erstellt. Natürlich wollen wir die erstellte Datei löschen, nachdem die Feature-Methode beendet ist. Wir können dies tun, indem wir die folgende Bereinigung hinzufügen block in unsere Feature-Methode:

import spock.lang.Specification

class FileSpec extends Specification {

	def 'Create a new file'() {

		setup:
		def file = new File("/tmp/foo.txt")
		
		when: 'A new file is created'
		file.createNewFile()
		
		then: 'Should create a new file'
		file.exists() == true 
		file.isFile() == true
		file.isDirectory() == false

		cleanup:
		file.delete()
    }
}

Wir haben uns nun kurz den Aufbau einer Feature-Methode angeschaut und unsere ersten Feature-Methoden geschrieben. Fassen wir zusammen, was wir aus diesem Blogbeitrag gelernt haben.

Zusammenfassung

Dieser Blogbeitrag hat uns vier Dinge gelehrt:

  • Eine Feature-Methode besteht aus Blöcken.
  • Jeder Block hat ein Label und der Hauptteil des Blocks erstreckt sich bis zum Anfang des nächsten Blocks oder bis zum Ende der Feature-Methode.
  • Eine Feature-Methode kann die folgenden Blöcke haben:setup , wann und dann , erwarten , Bereinigung , und wo .
  • Wir können einzelne Teile eines Blocks beschreiben, indem wir die und verwenden Etikett.

Der nächste Teil dieses Tutorials bietet eine Einführung in datengesteuertes Testen.

P.S. Sie können die Beispielanwendung dieses Blogbeitrags von Github herunterladen.


Java-Tag