Soorten Tests: Waarom Niet Beide?

0
32

Zo nu en dan een discussie laait op over de waarde van de getypte JavaScript. “Schrijf gewoon wat meer testen!” schreeuwen sommige tegenstanders. “Vervang de unit tests met typen!” schreeuwen de anderen. Beide zijn recht in sommige opzichten, en verkeerde in anderen. biedt weinig ruimte voor nuance. Maar in de ruimte van dit artikel kunnen we proberen om de lay-out van een met redenen omkleed argument voor hoe beide kunnen en moeten naast elkaar bestaan.

Juistheid: wat we echt willen

Het is het beste om te beginnen bij het einde. Wat we echt willen al deze meta-engineering op het einde is er juistheid. Ik bedoel niet de strikte theoretische informatica definitie van het, maar een meer algemene naleving van het gedrag van het programma te zijn specificatie: Wij hebben een idee van hoe ons programma zou moeten werken in ons hoofd, en het proces van het programmeren organiseert bits en bytes om dat idee in de werkelijkheid. Omdat we niet altijd precies te zijn over wat we willen, en omdat we willen graag het vertrouwen hebben dat onze programma niet breken als we een wijziging, dan schrijven we soorten en tests op de top van de ruwe code we hebben al schrijven om dingen te laten werken in de eerste plaats.

Dus, als we accepteren dat correctheid is wat we willen, en-soorten en tests zijn slechts geautomatiseerde manieren om er te komen, het zou geweldig zijn om een visueel model van hoe soorten en tests die ons helpen de juistheid, en daarom begrijpen waar ze elkaar overlappen en waar ze elkaar aanvullen.

Een visueel model van programma juistheid

Als we denken de hele oneindige Turing-compleet mogelijk ruimte van alles wat programma ‘ s ooit doen — inclusive van storingen — als een grote grijze vlakte, wat willen we met ons programma om te doen, onze specificatie, is een heel, heel, heel klein deel van die ruimte (het groene diamant hieronder overdreven in grootte omwille van de iets laten zien):

Onze taak in de programmering is te stoeien met onze programma zo dicht mogelijk bij de specificatie mogelijk (te weten, natuurlijk, we zijn onvolmaakt, en onze spec is voortdurend in beweging, bijvoorbeeld als gevolg van een menselijke fout, nieuwe functies of onder de opgegeven gedrag; dus we nooit helemaal in slagen precies overlappen):

Merk nogmaals op, dat de grenzen van onze programma ‘ s gedrag ook geplande en ongeplande fouten voor de toepassing van onze discussie hier. Onze betekenis van het “correcte” met geplande fouten, maar geldt niet voor onbedoelde fouten.

Tests en Juistheid

We schrijven tests om ervoor te zorgen dat onze programma past bij onze verwachtingen, maar hebben een aantal keuzes van dingen om te testen:

De ideale testen zijn de oranje stippen in de diagram — ze nauwkeurig te kunnen testen dat ons programma overlap kent met de spec. In deze visualisatie, we hebben niet echt een onderscheid maken tussen soorten tests, maar je kunt je voorstellen unit tests als heel kleine puntjes, terwijl integratie/end-to-end tests zijn grote stippen. Één van beide manier, zijn ze punten, want geen één test volledig wordt beschreven van elk pad door middel van een programma. (In feite, kunt u een 100% code coverage en nog steeds geen test elk pad, want van de combinatorische explosie!)

De blauwe punt in dit diagram is een slechte test. Zeker, het testen dat ons programma werkt, maar het eigenlijk niet vastmaken aan het onderliggende spec (wat willen we echt uit ons programma, aan het einde van de dag). Op het moment dat we een oplossing voor ons programma uit te lijnen dichter bij spec, deze test breekt, geeft ons een vals-positief.

De paarse stip is een waardevolle test omdat het testen hoe wij denken dat onze programma moet werken en identificeert een gebied waar onze programma dat op dat moment niet. Toonaangevend met paarse tests en de vaststelling van de implementatie van het programma dienovereenkomstig is ook bekend als Test-Driven Development.

De rode test in dit diagram is een zeldzame test. In plaats van de normale (oranje) tests die test “happy paden” (met inbegrip van de geplande fout staten), dit is een test die verwacht en controleert u dat “ongelukkig paden” fail. Als deze test “pasjes” waar het hoort “mislukt,” dat is een enorme vroege waarschuwing dat er iets mis ging — maar het is eigenlijk onmogelijk om genoeg tests ter dekking van de enorme uitgestrektheid van het mogelijke ongelukkig paden die zich buiten het groene spec gebied. Mensen zelden vinden het testen van die dingen die niet zou werken, niet werken, dus ze doen het niet; maar het kan nog steeds een nuttige vroege waarschuwing wanneer de dingen verkeerd gaan.

Soorten en Juistheid

Waar tests zijn enkele punten op de mogelijkheid om de ruimte van wat ons programma kunt doen, types vertegenwoordigen categorieën carving gehele secties van de totale ruimte. We kunnen visualiseren, zoals rechthoeken:

We kiezen een rechthoek om het contrast van de diamant vertegenwoordigen van het programma, omdat geen enkel type systeem, alleen kan het volledig beschrijven van onze programma ‘ gedrag met behulp van types alleen. (Kies een triviaal voorbeeld van een id dat moet altijd een positief geheel getal is een getal, maar het aantal type accepteert ook breuken en negatieve getallen. Er is geen manier om te zorgen dat een aantal type om een specifiek aanbod, buiten een zeer eenvoudige unie van het aantal letters.)

Soorten dienen als een beperking op waar onze programma kan gaan als je de code. Als ons programma begint om hoger zijn dan de opgegeven grenzen van uw programma ‘ s typen, onze type-checker (zoals de Schrijfmachine of Stroom) zal gewoon weigeren om ons te laten compileren van het programma. Dit is leuk, want in een dynamische taal als JavaScript, het is heel gemakkelijk om per ongeluk een neerstortend programma dat was zeker niet iets wat je bedoeld. De eenvoudigste waarde toevoegen is geautomatiseerd null controleren. Als foo heeft geen methode genaamd bar, dan bellen foo.bar() zal leiden tot de al te bekende undefined is niet een functie runtime-uitzondering. Als foo zijn getypt en op alle, dit kan zijn gevangen door de type-checker tijdens het schrijven, met specifieke verwijzing naar de problematische regel van de code (met automatisch aanvullen als een bijkomend voordeel). Dit is iets wat tests gewoon niet doen.

Wij zouden willen schrijven strikte vormen voor ons programma dat we proberen te schrijven en de kleinst mogelijke rechthoek die nog steeds past in onze specificaties. Echter, dit heeft een leercurve, omdat ten volle te profiteren van het type systemen gaat het leren van een geheel nieuwe syntaxis en de grammatica van operators en generic type logica die nodig is om het volledige dynamische bereik van JavaScript. Handboeken en Omzeilen van lagere deze leercurve, en meer investeringen nodig hier.

Gelukkig, deze aanneming/leercurve niet om ons te stoppen. Sinds type-checking is een opt-in proces met Stroom en configureerbare striktheid met Typoscript (met de mogelijkheid om selectief te negeren lastige regels code), hebben we ons kiezen uit een spectrum van type veiligheid. We kunnen zelfs dit model ook:

Grotere rechthoeken, zoals de big red one in de bovenstaande tabel, vertegenwoordigen een zeer tolerante goedkeuring van een type systeem op uw codebase — bijvoorbeeld, zodat implicitAny en volledig te vertrouwen op type inference om alleen maar beperken ons programma van de slechtste van onze codering.

Matige striktheid (zoals de middelgrote groene rechthoek) zou kunnen vertegenwoordigen een meer getrouw aan het typen, maar met veel noodluiken, zoals het gebruik van expliciete exemplaren van elk over de codebase en handmatige soort beweringen. Nog steeds is het mogelijk oppervlak van een geldig programma ‘ s die niet overeenkomen met onze spec is enorm verminderd, zelfs met dit licht te typen werk.

Maximale gestrengheid, zoals de paarse rechthoek, houdt de dingen zo strak om onze spec dat het soms ook andere delen van het programma die niet passen (en die zijn vaak onbedoelde fouten in uw programma en gedrag). Het vinden van bugs in bestaande programma ‘ s, zoals deze, is een veel voorkomende verhaal van teams omzetten van vanille JavaScript codebases. Echter, het krijgen van maximaal typ de veiligheid van onze type-checker waarschijnlijk impliceert het nemen van voordeel van generieke typen en speciale operatoren ontworpen om te verfijnen en het verkleinen van de mogelijke ruimte van soorten voor elke variabele en functie.

Merk op dat we niet technisch te schrijven van onze eerste programma voor het schrijven van de soorten. Immers, we willen gewoon onze types nauwkeurig model van onze spec, dus kunnen we schrijven onze typen en vervolgens vulgrond de uitvoering later. In theorie, zou dit Type-Driven Development; in de praktijk weinig mensen daadwerkelijk te ontwikkelen op deze manier, omdat soorten nauw doordringen en interleave met onze actuele programma-code.

Zet ze bij elkaar

Wat zijn we uiteindelijk tot het opbouwen van een intuïtieve visualisatie van hoe beide soorten en test elkaar aanvullen op het garanderen van onze programma ‘ s juistheid.

Onze Tests beweren dat onze programma specifiek voert als bedoeld in de toets selecteren van paden (hoewel er bepaalde andere varianten van de tests, zoals hierboven besproken, de overgrote meerderheid van de tests doen dit). In de taal van de visualisatie die we hebben ontwikkeld, “pin-code” de donkere groene diamant van ons programma aan het licht groene diamant van onze spec. Een beweging weg door onze programma breekt deze tests, waardoor ze krijsen. Dit is uitstekend! Tests zijn ook oneindig flexibel en kan worden geconfigureerd voor de meest aangepaste van use cases.

Onze Types beweren dat onze programma loopt niet echt van ons weg door te weigeren mogelijke faalwijzen dan een grens die we trekken, hopelijk net zo strak mogelijk rond onze spec. In de taal van onze visualisatie, ze “bevatten” de mogelijke drift van ons programma weg van onze spec (zoals we altijd imperfect zijn, en elke fout die we maken voegt extra fout gedrag van het programma). Soorten ook bot, maar krachtige (want van het type inference en editor tooling) tools die profiteren van een sterke gemeenschap leveren typen hoef je niet te schrijven vanaf nul.

In het kort:

  • Tests zijn best om ervoor te zorgen blij paden werken.
  • Types zijn het beste om te voorkomen ongelukkig paden van de bestaande.

Gebruik ze samen op basis van hun sterke punten, voor de beste resultaten!

Als u wilt om meer te lezen over hoe Soorten en Tests snijden, Gary Bernhardt is uitstekend praten over Grenzen en Kent C. Dodds’ Testen Trophy werden significante invloed op mijn denken voor dit artikel.