Test engineering, Testautomatisering, Tooling

Het ontwikkelen van software gaat altijd hand in hand met het testen ervan. Met testen kunnen we namelijk bewijzen dat onze software correct werkt. Maar wat verstaan we onder ‘correct werken’? Wat is de definitie van correct binnen software testen? Ik sta er uitgebreid bij stil in dit blogartikel.

Waarom correct nodig is

Wanneer we software testen, zijn er een aantal testen die moeten slagen. Slechts één falende test betekent dat we onze applicatie moeten aanpassen. Een test faalt wanneer een van de assertions incorrect is. Een test die niet kan falen is nutteloos. Dit zijn allemaal dingen die we als vanzelfsprekend beschouwen in de geaccepteerde termen. Ik heb echter gemerkt dat we niet stilstaan bij hoe we tot onze assertions komen, terwijl ze het belangrijkste deel van een test zijn.

Nu denk je wellicht “correct is natuurlijk gedefinieerd door requirements. Waar gaat dit heen, pannenkoek.” Daar heb je gelijk in, maar dat is alleen het geval in een perfecte wereld. In de echte wereld zijn requirements nooit compleet genoeg om elke assertion in onze test te definiëren. We moeten de gaten opvullen om tot een complete definitie van correct te komen. Om tot een complete definitie van correct te komen, starten we met de requirements. Verder gebruiken we onze ervaring, kennis en vaardigheden:

  • We gebruiken onze ervaring om veel voorkomende problemen te voorkomen en om onze kennis beter toe te passen.
  • We gebruiken onze kennis om erachter te komen waar de grootste risico’s zitten. Grotere risico’s hebben namelijk een betere definitie van correct nodig. Hiervoor gebruiken we onze kennis van hoe mensen werken (e.g. Ik zou deze knop missen), software development processen (e.g. Een feature ontwikkeld onder tijdsdruk), het business domein (e.g. Bankieren), de organisatie (e.g. Dit gaat iemands workflow kapot maken) en andere onderwerpen.
  • We gebruiken onze vaardigheden voor het verzamelen van data of kennis wanneer we deze nodig hebben. We gebruiken het ook om alles samen te voegen tot een samenhangend en bruikbaar geheel.

Nadat we correct hebben gedefinieerd, gebruiken we de definitie om assertions te maken voor onze tests.

Afbeelding1

Expliciete versus impliciete definities van correct

Geautomatiseerde tests dwingen ons om correct op te schrijven in de vorm van assertions in code. Elke geautomatiseerde test heeft een of meer assertions. Deze geschreven assertions zijn een expliciete definitie van correct.

Correct wordt minder vaak opgeschreven wanneer testers handmatig testen. Hier wordt vaak gestopt bij requirements. Dit betekent natuurlijk niet dat er helemaal geen definitie is. In plaats van het opschrijven, gebruikt de tester een impliciete definitie van correct. Impliciete definities zouden eigenlijk opgeschreven moeten worden zodat ze expliciet en – belangrijker -herbruikbaar worden. Ik erken echter ook dat dat niet altijd praktisch is.

Hoe definieer je correct

Er zijn twee fundamentele manieren om correct te definiëren: voorbeelden en regels. Deze zjjn fundamenteel omdat ze niet beschreven kunnen worden als iets anders dan zichzelf. Ze zijn de meest basale vorm van correct definiëren die ik ken. De omschrijvingen hieronder zijn niet heel gedetailleerd, maar zijn goed genoeg om een grof idee te geven.

Voorbeeld

Een voorbeeld heeft een specifieke situatie (= state en input) en correct resultaat (= output of side effect) nodig voor een specifieke Subject Under Test (SUT). Het correcte resultaat geldt alleen voor deze situatie en deze SUT. De test faalt als het correcte resultaat niet gelijk is aan het daadwerkelijke resultaat. Je kunt bewijzen dat een SUT correct werkt door dit meerdere keren te doen voor dezelfde SUt in verschillende situaties. Voorbeelden zijn veruit de meest gebruikte aanpak voor het definiëren van correct. Om een test te schrijven met dit type correct doen we het volgende:

  1. Maak een voorbeeld state
  2. Voer de Subject Under Test uit met voorbeeld input
  3. Assert of het daadwerkelijke resultaat hetzelfde is als het correcte resultaat

Deze stappen zou je kunnen herkennen als het AAA-patroon (Arrange, Act, Assert). Correct is gedefinieerd als een of meer assertions in de derde stap. Deze definitie geldt alleen voor dit voorbeeld, een ander voorbeeld heeft een andere definitie van correct en dus ook andere assertions.

Afbeelding2

Regel

Een regel is een herbruikbare bundel van logica, vaak met een mooie naam. Een regel wordt overtreden wanneer de input niet correct is volgens de logica van de regel. Een regel mag nooit gebroken worden, onafhankelijk van de state, input, of Subject Under Test (SUT). Een gebroken regel faalt de test. Je kunt bewijzen dat de SUT correct werkt door met meerdere regels te testen.

Omdat een regel nooit gebroken mag worden, zal een regel zichzelf soms uitzetten. Bijvoorbeeld, wanneer de regel niet relevant is bij de input. Zichzelf uitzetten zou onacceptabel zijn bij voorbeelden, maar is prima bij regels. Om een test te schrijven met dit type correct doen we het volgende:

  1. Schrijf een regel
  2. Voer de regel uit met een voorbeeld situatie (= state en input) en de Subject Under Test
  3. Assert of de regel is gebroken

Een voordeel van regels is herbruikbaarheid. Hierdoor kunnen we stap twee meerdere keren uitvoeren met gegeneerde input (e.g. Property-based testing). Het stelt ons ook in staat om regelsets te maken die we kunnen delen tussen applicaties en teams (e.g. Linters).

Afbeelding3

Correct op meerder manieren gebruiken

Met de fundamentele aanpakken van correct kunnen we andere testaanpakken beter begrijpen. Ik ben nog geen testaanpak tegengekomen die niet gedefinieerd kan worden als voorbeelden en/of regels.

De meeste niet-fundamentele aanpakken combineren correct met andere aspecten van software development. Bijvoorbeeld, Contract-based testing definieert correct met regels afgeleid van contracten. Daarnaast biedt het een gestructureerde manier voor teams om samen te werken en dezelfde taal te spreken.

Andere aanpakken combineren beide fundamentele aanpakken om tot een betere definitie van correct te komen. Bijvoorbeeld, Property-based testing definieert correct door een voorbeeld in een regel te verpakken. Het resultaat is vele regel-gegenereerde voorbeelden die samen een solide definitie van correct opleveren.

Er zijn nog veel meer aanpakken te verkennen. Ik ben bezig met een overzicht van testaanpakken waarbij regels en voorbeelden centraal staan. Dit zal helaas moeten wachten tot een volgend artikel.

Conclusie

Om te bewijzen dat onze software goed werkt, moeten we eerst definiëren wat correct is. Correct wordt gedefinieerd op basis van requirements, ervaring en vaardigheden. Soms definiëren we correct impliciet, maar het is beter als we het expliciet maken.

We kunnen correct definiëren op twee manieren: voorbeelden en regels. Voorbeelden vereisen een specifieke situatie en correct resultaat voor een Subject Under Test. Regels zijn herbruikbaar en mogen niet overtreden worden, onafhankelijk van de situatie of Subject Under Test. We kunnen veel interessante testaanpakken definiëren in termen van voorbeelden en regels. In een volgend artikel besteed ik hier graag aandacht aan.

Wil je ons nieuwste Paarsz magazine per post ontvangen? Laat dan je gegevens achter.

werkenbij overall

Werken bij Bartosz?

Vincent Verhelst

Geïnteresseerd in Bartosz? Dan ga ik graag met jou in gesprek. We kunnen elkaar ontmoeten met een kop koffie bij ons op kantoor. Of tijdens ontbijt, lunch, borrel of diner op een plek die jou het beste uitkomt. Jij mag het zeggen.

Mijn Paarsz