Specificaties schrijven met Kotlin en Spek

1. Inleiding

Specificatie Testkaders zijn complementair aan Unit Testing kaders voor het testen van onze applicaties.

In deze tutorial introduceren we het Spek-framework - een specificatietestframework voor Java en Kotlin.

2. Wat is specificatietesten?

Simpel gezegd, in Specification Testing beginnen we met de specificatie en beschrijven we de bedoeling van de software, in plaats van de mechanica ervan.

Dit wordt vaak gebruikt in Behavior Driven Development, aangezien het de bedoeling is om een ​​systeem te valideren op basis van vooraf gedefinieerde specificaties van onze applicatie.

Algemeen bekende specificatietestframeworks zijn onder meer Spock, Cucumber, Jasmine en RSpec.

2.1. Wat is Spek?

Spek is een op Kotlin gebaseerd specificatietestframework voor de JVM. Het is ontworpen om te werken als een JUnit 5-testengine. Dit betekent dat we het gemakkelijk kunnen aansluiten op elk project dat al JUnit 5 gebruikt om het naast andere tests uit te voeren.

Het is ook mogelijk om de tests uit te voeren met het oudere JUnit 4-framework, door indien nodig de JUnit Platform Runner-afhankelijkheid te gebruiken.

2.2. Afhankelijkheden van Maven

Om Spek te gebruiken, moeten we de vereiste afhankelijkheden toevoegen aan onze Maven-build:

 org.jetbrains.spek spek-api 1.1.5 test org.jetbrains.spek spek-junit-platform-engine 1.1.5 test 

De spek-api afhankelijkheid is de feitelijke API die wordt gebruikt voor het testraamwerk. Het definieert alles waarmee onze tests zullen werken. De spek-junit-platform-engine afhankelijkheid is dan de JUnit 5-testengine die nodig is om onze tests uit te voeren.

Merk op dat alle Spek-afhankelijkheden dezelfde versie als elkaar moeten hebben. De laatste versie is hier te vinden.

2.3. Eerste test

Als Spek eenmaal is opgezet, is het schrijven van tests een eenvoudig geval van het schrijven van de juiste klasse in de juiste structuur. Dit is enigszins ongebruikelijk om het leesbaarder te maken.

Spek vereist dat onze tests allemaal erven van een geschikte superklasse - meestal Spek - en dat we onze tests implementeren door een blok door te geven aan de constructor van deze klasse:

class FirstSpec: Spek ({// Implementeer de test hier})

3. Teststijlen

Specification Testing legt de nadruk op schrijftoetsen op een manier die zo leesbaar mogelijk is. Komkommer schrijft bijvoorbeeld de hele test in voor mensen leesbare taal en koppelt deze vervolgens aan stappen, zodat de code gescheiden blijft.

Spek werkt met behulp van speciale methoden die als leesbare tekenreeksen fungeren, die elk een blok krijgen om naar behoren uit te voeren. Er zijn enkele variaties op de functies die we gebruiken, afhankelijk van de manier waarop we willen dat de tests worden gelezen.

3.1. gegeven/Aan/het

Een manier waarop we onze tests kunnen schrijven is in de "gegeven / aan / it" -stijl.

Dit maakt gebruik van methoden genaamd gegeven, Aan en het, genest in die structuur, om onze tests te schrijven:

  • gegeven - stelt de beginvoorwaarden op voor de test
  • Aan - voer de testactie uit
  • het - beweren dat de testactie correct is uitgevoerd

We kunnen er zoveel van elk blok hebben als we nodig hebben, maar we moeten ze in deze volgorde nesten:

class CalculatorTest: Spek ({gegeven ("A calculator") {val calculator = Calculator () on ("Optellen van 3 en 5") {val resultaat = calculator.add (3, 5) it ("Produceert 8") {assertEquals (8, resultaat)}}}})

Deze test leest heel gemakkelijk. Als we ons concentreren op de teststappen, kunnen we het lezen als "Gegeven een rekenmachine, bij het optellen van 3 en 5 levert het 8 op".

3.2. beschrijven/het

De andere manier waarop we onze tests kunnen schrijven, is in de "beschrijf / it" -stijl. In plaats daarvan wordt de methode gebruikt beschrijven voor al het nesten, en blijft gebruiken het voor onze beweringen.

In dit geval kunnen we de beschrijven methoden zoveel als we nodig hebben om onze tests te schrijven:

class CalculatorTest: Spek ({beschrijven ("A calculator") {val calculator = Calculator () beschrijven ("Optellen") {val resultaat = calculator.add (3, 5) it ("Produceert het juiste antwoord") {assertEquals ( 8, resultaat)}}}})

Er wordt minder structuur opgelegd aan de tests die deze stijl gebruiken, wat betekent dat we veel meer flexibiliteit hebben bij het schrijven van de tests.

Helaas is het nadeel hiervan dat de tests niet zo natuurlijk lezen als wanneer we "gegeven / aan / het" gebruiken.

3.3. Extra stijlen

Spek dwingt deze stijlen niet af, en het zorgt ervoor dat de trefwoorden zoveel als gewenst kunnen worden uitgewisseld. De enige vereisten zijn dat alle beweringen binnen een het en dat er op dat niveau geen andere blokken worden gevonden.

De volledige lijst met beschikbare nestbare trefwoorden is:

  • gegeven
  • Aan
  • beschrijven
  • context

We kunnen deze gebruiken om onze tests de best mogelijke structuur te geven voor hoe we ze willen schrijven.

3.4. Gegevensgestuurde tests

Het mechanisme dat wordt gebruikt voor het definiëren van tests zijn allemaal niets meer dan eenvoudige functieaanroepen. Dit betekent dat we er andere dingen mee kunnen doen, zoals elke normale code. In het bijzonder kunnen we ze datagedreven aanroepen als we dat willen.

De eenvoudigste manier om dit te doen, is door de gegevens die we willen gebruiken te herhalen en het juiste blok aan te roepen vanuit deze lus:

class DataDrivenTest: Spek ({beschrijven ("Een gegevensgestuurde test") {mapOf ("hallo" tegen "HELLO", "wereld" tegen "WERELD"). voor elke {invoer, verwacht -> beschrijven ("Hoofdlettergebruik $ invoer") {it ("Retourneert correct $ verwacht") {assertEquals (verwacht, input.toUpperCase ())}}}}})

We kunnen van alles zoals dit doen als dat nodig is, maar dit is waarschijnlijk het nuttigst.

4. Beweringen

Spek schrijft geen specifieke manier voor om beweringen te gebruiken. In plaats daarvan, het stelt ons in staat om het assertion framework te gebruiken waar we het meest comfortabel mee zijn.

De voor de hand liggende keuze is de org.junit.jupiter.api.Assertions class, aangezien we het JUnit 5-framework al gebruiken als onze testrunner.

We kunnen echter ook elke andere assertion-bibliotheek gebruiken die we willen als deze onze tests beter maakt, bijvoorbeeld Kluent, Expekt of HamKrest.

Het voordeel van het gebruik van deze bibliotheken in plaats van de standaard JUnit 5 Beweringen klasse is te danken aan de leesbaarheid van de tests.

De bovenstaande test, herschreven met Kluent, luidt bijvoorbeeld als:

class CalculatorTest: Spek ({beschrijven ("A calculator") {val calculator = Calculator () beschrijven ("Optellen") {val resultaat = calculator.add (3, 5) it ("Produceert het juiste antwoord") {result shouldEqual 8}}}})

5. Voor / na afhandelaars

Zoals bij de meeste testframeworks, Spek kan ook logica voor / na tests uitvoeren.

Dit zijn, precies zoals hun naam aangeeft, blokken die voor of na de test zelf worden uitgevoerd.

De mogelijkheden hier zijn:

  • beforeGroup
  • afterGroup
  • beforeEachTest
  • afterEachTest

Deze kunnen in elk van de geneste trefwoorden worden geplaatst en zijn van toepassing op alles binnen die groep.

Zoals Spek werkt, wordt alle code binnen een van de geneste sleutelwoorden onmiddellijk bij het begin van de test uitgevoerd, maar de besturingsblokken worden in een bepaalde volgorde uitgevoerd, gecentreerd rond de het blokken.

Werkend van buiten naar binnen, voert Spek elk uit beforeEachTest blok onmiddellijk voor elke het blok genest in dezelfde groep, en elk afterEachTest blok onmiddellijk na elke het blok. Evenzo zal Spek elk uitvoeren beforeGroup blok onmiddellijk voor elke groep en elk afterGroup blok onmiddellijk na elke groep in de huidige nesting.

Dit is ingewikkeld en kan het beste worden uitgelegd met een voorbeeld:

class GroupTest5: Spek ({beschrijven ("Outer group") {beforeEachTest {System.out.println ("BeforeEachTest 0")} beforeGroup {System.out.println ("BeforeGroup 0")} afterEachTest {System.out.println ( "AfterEachTest 0")} afterGroup {System.out.println ("AfterGroup 0")} beschrijven ("Inner group 1") {beforeEachTest {System.out.println ("BeforeEachTest 1")} beforeGroup {System.out.println ("BeforeGroup 1")} afterEachTest {System.out.println ("AfterEachTest 1")} afterGroup {System.out.println ("AfterGroup 1")} it ("Test 1") {System.out.println (" Test 1 ")}}}})

De output van het bovenstaande is:

Voor Groep 0 Voor Groep 1 Voor Elke Test 0 Voor Elke Test 1 Test 1 Na Elke Test 1 Na Elke Test 0 Na Groep 1 Na Groep 0

We kunnen meteen zien dat de buitenkant beforeGroup / afterGroup blokken zijn rond de hele reeks tests, terwijl de binnenste beforeGroup / afterGroup blokken zijn alleen rond de tests in dezelfde context.

We kunnen ook zien dat alle beforeGroup blokken worden uitgevoerd vóór elke beforeEachTest blokken en het tegenovergestelde voor afterGroup / afterEachTest.

Een groter voorbeeld hiervan, dat de interactie tussen meerdere tests in meerdere groepen laat zien, is te zien op GitHub.

6. Proefpersonen

Vaak zullen we een enkele specificatie schrijven voor een enkel testonderwerp. Spek biedt een handige manier om dit te schrijven, zodat het de Subject Under Test automatisch voor ons beheert. Wij gebruiken de SubjectSpek basisklasse in plaats van de Spek klasse hiervoor.

Wanneer we dit gebruiken, moeten we een oproep doen aan het onderwerpen blok op het buitenste niveau. Dit definieert de proefpersoon. We kunnen hier dan vanuit een van onze testcodes naar verwijzen als onderwerpen.

We kunnen dit gebruiken om onze eerdere rekenmachinetest als volgt te herschrijven:

class CalculatorTest: SubjectSpek ({subject {Calculator ()} beschrijven ("Een rekenmachine") {beschrijven ("Optellen") {val resultaat = subject.add (3, 5) it ("Produceert het juiste antwoord") {assertEquals ( 8, resultaat)}}}})

Het lijkt misschien niet veel, maar dit kan helpen om de tests een stuk leesbaarder te maken, vooral als er een groot aantal testgevallen moet worden overwogen.

6.1. Afhankelijkheden van Maven

Om de Subject Extension te gebruiken, moeten we een afhankelijkheid toevoegen aan onze Maven-build:

 org.jetbrains.spek spek-subject-extension 1.1.5 test 

7. Samenvatting

Spek is een krachtig raamwerk dat een aantal zeer leesbare tests mogelijk maakt, wat op zijn beurt betekent dat alle delen van de organisatie ze kunnen lezen.

Dit is belangrijk om alle collega's een bijdrage te laten leveren aan het testen van de gehele applicatie.

Ten slotte zijn codefragmenten, zoals altijd, te vinden op GitHub.