De Kijk van Kees: De Master

Redactie: Eric Spaargaren

Auteur: Kees Blokland ● kees.blokland@polteq.com

We zijn al een jaar op weg met het bouwen van een systeem, hebben nog onvoldoende geautomatiseerde tests en we moeten binnen een jaar een goed werkend systeem opleveren aan meer dan tienduizend gebruikers. Hoe zorgen we ervoor dat de dekking van de geautomatiseerde tests tijdig op orde is?

Om te beginnen kijken we naar hoe en waar we nuttige dekking kunnen realiseren. Het systeem bestaat uit een web-based user interface (UI), een backend (BE) en een database. We onderdrukken de reflex om snel veel functionele UI-tests te bouwen. Dat lijkt aantrekkelijk omdat het zien werken van het systeem in de vorm waarin de gebruiker dat gaat ervaren, het acceptatieproces vergemakkelijkt.

Maar het is om verschillende redenen helaas niet haalbaar om hiermee een gedetailleerde dekking te realiseren:

  • het ontwikkelen en onderhouden van de tests zijn tijdrovend; het nabouwen van de gebruikershandelingen is arbeidsintensief en moet worden onderhouden wanneer de UI wijzigt.
  • het draaien van de tests duurt relatief lang; de ervaring leert dat een grofmazige testset van een redelijk complex systeem zo maar een uur duurt, wat erg lang is voor een continuous integration (CI) proces

De behoefte aan een fijnmazige functionele test vervullen we bij voorkeur door de UI te passeren en direct op de API’s van de BE te testen. De UI is vooral een doorgeefluik en bevat weinig van de essentiële bedrijfslogica, dus als we de grofmazige UI-gebaseerde tests aanvullen met fijnmaziger API-tests bereiken we toch een hoge functionele testdekking. Het voordeel van API-tests is namelijk dat ze veel sneller zijn. Tests die via de UI minuten zouden kosten, verlopen direct op de API van de BE in seconden.

Het bouwen van API tests kost natuurlijk wel tijd, maar als de API-documentatie goed op orde is, bijvoorbeeld met behulp van swagger, kunnen we snel veel tests maken. We hebben hier nog wel een uitdaging om geautomatiseerde tests te ontwikkelen voor alle API’s die in het afgelopen jaar al zijn opgeleverd. De product owner heeft hiervoor alle opgeleverde functionele EPIC’s in volgorde van prioriteit gezet – waarbij vooral het productrisico-niveau is meegewogen. We bouwen dus de functionele API-tests in volgorde van prioriteit.

We hebben dan nog niet voldoende risico’s afgedekt. Want als we nog detetailleerder kijken naar het systeem, op het niveau van de programmacode, dan merken we dat ook de API-tests nog te grofmazig zijn om veel, liefst alle, paden door de software met een test te dekken. Daarom willen we geautomatiseerde unit tests voor kleine porties van de programmacode. Als een test faalt is ook meteen duidelijk waar de oorzaak van het probleem gezocht moeten worden. Unit tests zijn klein, we hebben er al vrij snel honderden tot duizenden nodig, maar ze zijn ook snel, je kunt denken aan milliseconden per test. Dit hangt wel af van de testbaarheid van het systeem. Soms hebben we mocks nodig voor een unit test, zoals een database, die een test behoorlijk kan vertragen. Liever willen we geen tests die afhankelijk zijn van een trage mock, maar soms lopen we tegen beperkingen aan. Dit soort unit tests hebben dan meer het karakter van integratietests. In ons project hebben we hier helaas me te maken.

In het afgelopen jaar waren er wel een dikke duizend BE unit-tests gemaakt, maar die konden alleen door ontwikkelaars geïsoleerd, per stuk gedraaid worden. In het bouwproces bleken veel unit-tests onderling afhankelijk te zijn waardoor ze nooit allemaal slaagden. Hierdoor waren de tests niet bruikbaar in het CI software-bouwproces. En dat willen we wel: alle unit-testen moeten slagen als voorwaarde voor het ’mergen’ van code naar de ’development master’. Want op die manier voorkomen we regressie in de software die klaar staat voor oplevering. Om een goede dekking te krijgen in de unit-tests lopen inmiddels de volgende maatregelen:

  • onafhankelijk maken van de unit-tests.
  • repareren van falende tests (of het oplossen van bugs).
  • nieuwe software wordt altijd opgeleverd met unit-test code.
  • een aantal gebieden in de software heeft prioriteit gekregen, ook weer op basis van productrisico, de unit-testdekking moet daar sterk omhoog.
  • de unit-tests die betrouwbaar zijn, staan ’aan’ in het bouwproces: een niet slagende unit-test faalt de bouw.

Nu groeit de testdekking van de tests op alle niveaus, maar toch mist nog een belangrijke schakel in de weg naar succes: niet alleen de unit-tests maar ook de andere tests moeten een plaats krijgen in de geautomatiseerde terugkoppellussen van de bouwpijplijnen, zodat we borgen dat de software in de development master branch succesvol door alle inmiddels geautomatiseerde tests loopt. We zijn bijna zover dat de API-tests volledig meedraaien als voorwaarde voor een geslaagde bouw. De UI-gebaseerde testset vinden we te lang duren om op te nemen als voorwaardelijk in het bouwproces: het duurt dan te lang voordat een ontwikkelaar ziet of de merge geslaagd is. De UI tests draaien we iedere nacht achteraf.

De allerlaatste stap die het team nu nog moet zetten is om iedere ochtend naar de resultaten van de nachtelijke testrun te kijken en met prio eerst falende tests oplossen op de development master branch. Want die is heilig!

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *