Integratietesten in Flutter

Om de integratie van de UX met de logica van de frontend te testen hebben we in mijn huidige project Flutter widget test framework gebruikt. Dit bleek een heel leerzame ervaring.

Om volledige controle over de applicatie te hebben, draaien onze integratietesten de volledige applicatie in een gecontroleerde omgeving. Hiervoor hebben we de met Provider geïnjecteerde backend communicatie component te vervangen door een mock. Door extensies op WidgetTester, een TestBackend (die de backend communicatie mock bestuurt) en een TestPlatform (die de platform integraties mockt), hebben we een sandbox gerealiseerd die de volledige besturing van de applicatie mogelijk maakt.

Net als bij de widget testen, hebben we verschillende extensies op WidgetTester gebouwd. Zo is er een gedeelde extensie die naar het gewenste scherm navigeert, en elke test file heeft een lokale extensie die gedeelde interacties voor die context levert. Daarnaast worden in de integratietesten ook onze normale widget test extensies gebruikt. Een typisch voorbeeld is een helper die een kebab menu opent binnen een finder, en vervolgens menu opties op basis van widget keys selecteert. Hierdoor ontstaat een DSL voor de interactie met de applicatie die de leesbaarheid van de testen sterk vergroot.

Omdat onze widget testen zelf een hoge dekking hebben, ligt de focus van de integratietesten op interacties die widgets met logica en communicatie integreren. Dit levert testen die de happy flow van gebruikers uitvoeren, met asserties op symptomen die de correcte samenverwerking van classes bevestigen. De dekking van zowel de uitvoering als asserties is hiermee niet extreem hoog, maar dat wordt ruim gecompenseerd door de hoge dekking van de widget en unit testen. Het doel van integratietesten is immers om de integratie te testen.

Om de integratietesten compact te houden, hebben we als extensie op WidgetTester een recursieve startIn(Widget screen) functie geschreven, die de app naar het gewenste scherm navigeert. Door deze functie in de set up van elke integratie test aan te roepen, begint de test in het juiste scherm en met een minimale context.

Een handig hulpmiddel tijdens het schijven van integratietesten blijkt het runnen van individuele test files als applicatie:

$ flutter run -d <target_device> <volledige_pad_naar_test_file>

Hiermee worden de testen als applicatie met user interface uitgevoerd. Op deze manier is snel te herkennen of de bedoelde interacties in de test ook werkelijk worden uitgevoerd. Dat is vooral handig omdat de foutmeldingen van het test framework wel melden dat een widget niet gevonden is, maar niet aangeven wat er wel (of juist niet) is gebeurd. Handig is hierbij de mogelijkheid om de testen (met hot reload) opnieuw te draaien, en allerlei informatie over de app op te vragen. Door op de lopende app te klikken levert de console als bonus suggesties welke finders de widgets op die locatie vinden.

Doordat de integratietesten synchroon draaien, is het mogelijk om testen te debuggen door breakpoints te zetten op plaatsen die door de test geraakt zouden moeten worden.