Je JavaScript testen met Jasmine

We weten allemaal dat we onze code moeten testen, maar dat doen we niet echt. Ik denk dat het eerlijk is om te zeggen dat de meesten van ons het uitstellen omdat, negen van de tien keer, het betekent om nog een ander concept te leren. In deze zelfstudie laat ik je kennismaken met een geweldig klein kader voor het eenvoudig testen van je JavaScript-code.

Wist je trouwens dat je je JavaScript-fouten snel en eenvoudig kunt laten oplossen door een expert op Envato Studio??

ThemeManiac, bijvoorbeeld, zal JavaavaScript-fouten of browsercompatibiliteitsproblemen op uw website of webtoepassing oplossen. De fixes kunnen zeer snel worden voltooid, op basis van de complexiteit en beschikbare informatie. Hij kan uw scripts ook reorganiseren en een geheel nieuwe gebruikerservaring maken. Hij heeft meer dan 1.000 banen op Envato Studio voltooid, met 99% van de klanten die hem aanbevelen.


Stap 0: BDD begrijpen

Vandaag gaan we leren over het Jasmine BDD-testraamwerk. Maar we stoppen hier eerst voor een omweg, om heel kort te praten, over BDD en TDD. Als u niet bekend bent met deze acroniemen, staan ​​ze voor Gedraggedreven ontwikkeling en Test gedreven ontwikkeling. Ik ben in het midden van het leren over wat elk van deze in de praktijk is en hoe ze verschillen, maar hier zijn enkele van de fundamentele verschillen:

BDD en TDD? staan ​​voor Gedraggedreven ontwikkeling en Test gedreven ontwikkeling.

TDD in zijn eenvoudigste vorm is alleen dit:

  1. Schrijf uw tests
  2. Zie ze falen
  3. Laat ze passeren
  4. Refactor
  5. Herhaling

Dat is vrij gemakkelijk te begrijpen, eh?

BDD is een beetje ingewikkelder: zoals ik het nu begrijp, denk ik niet dat jij of ik als een enkele ontwikkelaar het echt volledig kan beoefenen; het is meer een teamding. Hier zijn een paar van de oefeningen van BDD:

  • Het vaststellen van de doelen van verschillende belanghebbenden die nodig zijn om een ​​visie te implementeren
  • Betrokkenheid van stakeholders bij het implementatieproces door externe softwareontwikkeling
  • Aan de hand van voorbeelden om het gedrag van de toepassing of van code-eenheden te beschrijven
  • Deze voorbeelden automatiseren voor snelle feedback en regressietests

Voor meer informatie, kunt u het uitgebreide Wikipedia-artikel lezen (waaruit deze punten zijn overgenomen).

Dit alles om te zeggen dat, terwijl Jasmine zichzelf als een BDD-raamwerk beschouwt, we het op een meer TDD-achtige manier zullen gebruiken. Dat betekent echter niet dat we het verkeerd gebruiken. Als we klaar zijn, kun je je JavaScript eenvoudig testen? en ik verwacht dat je het doet!


Stap 1: de syntaxis leren

Jasmine neemt veel signalen van Rspec op.

Als je helemaal bekend bent met Rspec, is de de facto BDD-framework, je zult zien dat Jasmine veel signalen van Rspec opneemt. Jasmine-testen bestaan ​​hoofdzakelijk uit twee delen: beschrijven blokken en het blokken. Laten we kijken hoe dit werkt.

We zullen enkele realistischer tests van dichterbij bekijken, maar voorlopig houden we het eenvoudig:

beschrijven ('operator voor JavaScript-toevoeging', functie () it ('voegt twee getallen bij elkaar toe', functie () verwachten (1 + 2) .toEqual (3);););

Beide beschrijven en het functies nemen twee parameters: een tekstreeks en een functie. De meeste testkaders proberen zoveel mogelijk Engels te lezen, en je kunt dit zien met Jasmine. Merk allereerst op dat de string is doorgegeven aan beschrijven en de string doorgegeven aan het vorm een ​​zin (van soorten):? JavaScript-toevoeging operator voegt twee nummers bij elkaar.? Vervolgens laten we zien hoe.

Binnen dat het blokkeren, kunt u alle setup-code schrijven die u nodig hebt voor uw test. We hebben geen behoefte aan dit eenvoudige voorbeeld. Als u klaar bent om de eigenlijke testcode te schrijven, begint u met de verwachten functie, doorgeven wat je aan het testen bent. Merk op hoe dit ook een zin vormt: wij? Verwachten 1 + 2 tot en met 3.?

Maar ik loop zelf vooruit. Zoals ik al zei, ongeacht de waarde waar je aan voorbijgaat verwachten zal worden getest. De methode die u aanroept, de geretourneerde waarde van verwachten, wordt bepaald door welke test wordt uitgevoerd. Deze groep methoden wordt 'matchers' genoemd en we zullen er vandaag een aantal bekijken. In dit geval gebruiken we de gelijk maken matcher, die controleert of de waarde is doorgegeven verwachten en de waarde die is doorgegeven aan gelijk maken hebben dezelfde waarde.

Ik denk dat je klaar bent om dit naar een hoger niveau te tillen, dus laten we een eenvoudig project opzetten met Jasmine.


Stap 2: Een project opzetten

Jasmine kan alleen worden gebruikt; of je kunt het integreren met een Rails-project. We zullen de eerste doen. Hoewel Jasmine buiten de browser kan draaien (denk aan Node, onder andere), kunnen we een heel leuk sjabloon krijgen met de download.

Ga dus naar de stand-alone downloadpagina en ontvang de nieuwste versie. Je zou zoiets als dit moeten krijgen:

Je zult de actuele Jasmine framework-bestanden vinden in de lib map. Als u uw projecten liever anders structureert, doe dat dan; maar we houden dit voor nu.

Er is eigenlijk een voorbeeldcode in deze projectsjabloon. De daadwerkelijke? JavaScript (de code die we willen testen) is te vinden in de src subdirectory; we zullen de onze daar binnenkort neerzetten. De testcode - de bril-ga in de spec map. Maak je geen zorgen over de SpecHelper.js bestand gewoon nog; we komen hier op terug.

Dat SpecRunner.html bestand is wat de tests uitvoert in een browser. Open het (en vink het selectievakje? Gepasseerd? In de rechterbovenhoek aan), en je zou zoiets als dit moeten zien:

Dit laat ons zien dat alle tests voor het voorbeeldproject worden uitgevoerd. Zodra je deze tutorial hebt doorgenomen, raad ik je aan om de spec / PlayerSpec.js bestand en doorzoek die code. Maar laten we deze test eens een keer proberen.

  • creëren convert.js in de src map.
  • creëren convertSpec.js in de spec map,
  • Kopieer de SpecRunner.html bestand en hernoem het SpecRunner.original.html.
  • Verwijder de koppelingen naar de voorbeeldprojectbestanden in SpecRunner.html en voeg deze regels toe:

Nu zijn we klaar om een ​​minibibliotheek te maken die tussen meeteenheden omzet. We beginnen met het schrijven van de tests voor onze minibibliotheek.


Stap 3: De tests schrijven

Dus laten we onze tests schrijven, zullen wij?

beschrijven ("Convert library", function () beschrijven ("distance converter", function () ); beschrijven ("volume-omzetter", functie () ););

We beginnen hiermee; we testen onze Converteren bibliotheek. Je zult merken dat we aan het nestelen zijn beschrijven verklaringen hier. Dit is volkomen legaal. Het is eigenlijk een prima manier om afzonderlijke functies van dezelfde codebase te testen. In plaats van twee aparte beschrijven vraagt ​​om Converteren bibliotheek conversie op afstand en volume conversies, kunnen we een meer beschrijvend pakket van tests als deze.

Nu, op de eigenlijke tests. Ik zal het innerlijke herhalen beschrijven belt hier voor uw gemak.

beschrijven ("distance converter", functie () it ("converteert inches naar centimeters", function () verwachten (converteren (12, "in"). naar ("cm")). naarqual (30.48);) ; it ("converteert centimeters naar yards", function () expect (Convert (2000, "cm"). to ("yards")). toEqual (21.87);););

Dit zijn onze tests voor conversies op afstand. Het is belangrijk om hier iets op te merken: we hebben geen code geschreven voor onze Converteren bibliotheek, dus in deze tests doen we meer dan alleen controleren of het werkt: we beslissen eigenlijk hoe het zal worden gebruikt (en daarom geïmplementeerd). Dit is hoe we hebben besloten onze conversies te maken:

Converteren(, ).naar();

Ja, ik volg de manier waarop Jasmine de tests heeft geïmplementeerd, maar ik vind het een leuk formaat. Dus in deze twee tests heb ik de conversies zelf gemaakt (ok, met een rekenmachine) om te zien wat de resultaten van onze oproepen zouden moeten zijn. We gebruiken de gelijk maken matcher om te zien of onze tests slagen.

Dit zijn de volumetests:

beschrijf ("volumeconvertor", functie () it ("converteert liters naar gallons", functie () verwacht (Convert (3, "liter"). to ("gallons")). toEqual (0.79);) ; it ("converteert gallons naar cups", function () expect (Convert (2, "gallons"). to ("cups")). toEqual (32);););

En ik ga nog twee tests toevoegen in ons topniveau beschrijven bellen:

it ("gooit een foutmelding wanneer een onbekend van-eenheid wordt doorgegeven", functie () var testFn = function () Convert (1, "dollar"). naar ("yens"); verwacht (testFn) .toThrow ( nieuwe fout ("niet-herkende eenheid"));); it ("gooit een foutmelding wanneer een onbekende naar-eenheid wordt doorgegeven", function () var testFn = function () Convert (1, "cm"). to ("furlongs"); expect (testFn) .toThrow ( nieuwe fout ("niet-herkend naar-eenheid")););

Deze controleren op fouten die moeten worden gegenereerd wanneer onbekende eenheden worden doorgegeven aan de Converteren functie of de naar methode. U zult merken dat ik de daadwerkelijke conversie in een functie inpakkeer en die doorgeeft aan de verwachten functie. Dat komt omdat we de functie niet kunnen noemen als de verwachten parameter; we moeten het een functie geven en het de functie zelf laten noemen. Omdat we daar een parameter aan moeten doorgeven naar functie, we kunnen het op deze manier doen.

Het andere ding om op te merken is dat ik een nieuwe matcher introduceer: gooien, die een foutobject neemt. We zullen binnenkort meer matchers bekijken.

Nu, als u opent SpecRunner.html in een browser krijg je dit:

Super goed! Onze tests falen. Laten we nu onze openen convert.js bestand en werk wat:

functie Convert (nummer, fromUnit) var conversions = distance: meters: 1, cm: 0.01, feet: 0.3048, inches: 0.0254, yards: 0.9144, volume: liters: 1, gallons: 3.785411784, cups: 0.236588236 , tussenUnit = false, type, eenheid; for (typ conversies in) if (conversies (type)) if () (unit = conversies [type] [fromUnit])) betweenUnit = nummer * eenheid * 1000;  return to: function (toUnit) if (betweenUnit) for (typ in conversions) if (conversions.hasOwnProperty (type)) if ((unit = conversies [type] [toUnit])) ga terug fix (betweenUnit / (unit * 1000));  gooi nieuwe fout ("niet herkend naar-eenheid");  else gooi nieuwe fout ("niet-herkende van-eenheid");  function fix (num) return parseFloat (num.toFixed (2)); ; 

We gaan dit niet echt bespreken, omdat we Jasmine hier leren. Maar hier zijn de belangrijkste punten:

  • We voeren de conversies door de conversie in een object op te slaan; conversiecijfers worden ingedeeld op type (afstand, volume, voeg uw eigen toe). Voor elk meetgebied hebben we een basiswaarde (meters of liters, hier) waar alles naar converteert. Dus als je het ziet meter: 0.9144, je weet dat dat is hoeveel meters er in een meter zijn. Vervolgens, om werven te converteren naar bijvoorbeeld centimeters, vermenigvuldigen we ons yards door de eerste parameter (om het aantal meters te krijgen) en deel het product vervolgens in cm, het aantal meter in een centimeter. Op deze manier hoeven we de conversiepercentages niet voor elk paar waarden op te slaan. Dit maakt het ook gemakkelijk om later nieuwe waarden toe te voegen.
  • In ons geval verwachten we dat de doorgegeven eenheden hetzelfde zijn als de sleutels die we gebruiken in de conversietabel.? Als dit een echte bibliotheek is, willen we meerdere indelingen ondersteunen, zoals 'in', 'inch' en 'inches'-en daarom willen we wat logica toevoegen om overeen te komen met de fromUnit naar de juiste sleutel.
  • Aan het einde van de Converteren functie bewaren we de tussenwaarde in betweenUnit, die is geïnitialiseerd naar vals. Op die manier, als we het niet hebben fromUnit, betweenUnit zal zijn vals naar binnen gaan naar methode, en dus een fout met worden gegooid.
  • Als we het niet hebben toUnit, er zal een andere fout worden gegenereerd. Anders zullen we delen zoals nodig en de geconverteerde waarde retourneren.

Ga nu terug naar SpecRunner.html en laad de pagina opnieuw. U zou dit nu moeten zien (na controle? Weergeven geslaagd?):

Daar ga je! Onze tests zijn voorbij. Als we hier een echt project zouden ontwikkelen, zouden we tests schrijven voor een bepaald stuk functionaliteit, ze laten slagen, tests schrijven voor een nieuwe controle, ze passen, etc. Maar omdat dit een eenvoudig voorbeeld was, hebben we het net gedaan alles in één klap.

En nu u dit eenvoudige voorbeeld van het gebruik van Jasmine hebt gezien, laten we nog een paar andere functies bekijken die het u biedt.


Stap 4: De matchers leren

Tot nu toe hebben we twee matchers gebruikt: gelijk maken en gooien. Er zijn natuurlijk nog veel meer. Hier zijn er een paar die u waarschijnlijk nuttig zult vinden; je kunt de hele lijst op de wiki zien.

toBeDefined / toBeUndefined

Als u alleen zeker wilt weten dat een variabele of eigenschap is gedefinieerd, is daar een matcher voor. Er is ook een om te bevestigen dat een variabele of een property is onbepaald.

it ("is defined", function () var name = "Andrew"; expect (name) .toBeDefined ();) it ("is not defined", function () var name; expect (name) .toBeUndefined (););

toBeTruthy / toBeFalsy

Als iets waar of onwaar is, zullen deze matchers het doen.

it ("is true", function () expect (Lib.isAWeekDay ()). toBeTruthy ();); it ("is false", function () expect (Lib.finishedQuiz) .toBeFalsy (););

ToBeLessThan / toBeGreaterThan

Voor alles wat u mensen noemt. Je weet hoe deze werken:

it ("is less than 10", function () expect (5) .toBeLessThan (10);); it ("is groter dan 10", function () expect (20) .toBeGreaterThan (10););

toMatch

Hebt u wat uitvoertekst die moet overeenkomen met een reguliere expressie? De toMatch matcher is klaar en bereid.

it ("output the right text", function () expect (cart.total ()). toMatch (/ \ $ \ d *. \ d \ d /););

toContain

Deze is best handig. Het controleert of een array of tekenreeks een item of substring bevat.

it ("should contain sinaasanges", function () expect ([sinaasappels]);;;

Er zijn ook een paar andere matchers die je in de wiki kunt vinden. Maar wat als je een matcher wilt die niet bestaat? Echt, je zou zo ongeveer alles moeten kunnen doen met een bepaalde set-up code en de matchers die Jasmine biedt, maar soms is het leuker om een ​​deel van die logica te abstraheren om een ​​beter leesbare test te hebben. Serieus (nou ja, eigenlijk niet), Jasmine stelt ons in staat om onze eigen matchers te maken. Maar om dit te doen, zullen we eerst iets anders moeten leren.

Stap 5: Vóór en na

Vaak, wanneer u een codebasis test, wilt u voor elke test in een reeks enkele regels met instellingscode uitvoeren. Het zou pijnlijk en uitgebreid zijn om dat voor elke kopie te moeten kopiëren het Bel ons, dus Jasmine heeft een handige kleine functie waarmee we code kunnen aanwijzen die vóór of na elke test moet worden uitgevoerd. Laten we eens kijken hoe dit werkt:

beschrijven ("MyObject", functie () var obj = new MyObject (); beforeEach (function () obj.setState ("clean");); it ("changes state", function () obj.setState ("vies"); verwacht (obj.getState ()). toEqual ("dirty");) it ("voegt states" toe, function () obj.addState ("packaged"); expect (obj.getState ( )). tot Kwal (["schoon", "verpakt"]);));

In dit gekunstelde voorbeeld kunt u zien hoe, voordat elke test wordt uitgevoerd, de status van obj is ingesteld op? opschonen ?. Als we dit niet hebben gedaan, blijven de wijzigingen aan een object in een vorige test standaard doorgaan naar de volgende test. Natuurlijk kunnen we ook iets soortgelijks doen met de Na elke functie:

beschrijven ("MyObject", functie () var obj = nieuw MyObject ("clean"); // stelt de initiële status afterEach (function () obj.setState ("clean");); it ("changes state" in , function () obj.setState ("dirty"); expect (obj.getState ()). toEqual ("dirty");) it ("adds states", function () obj.addState ("packaged" ); verwacht (obj.getState ()). toEqual (["clean", "packaged"]);));

Hier beginnen we met het instellen van het object om het vervolgens te corrigeren na elke test. Als je het wilt MijnObj functie, zodat je deze code een keer kunt proberen, je kunt hem hier in een GitHub-gist krijgen.

Stap 6: aangepaste matchers schrijven

Zoals we eerder al zeiden, zouden matchers van klanten waarschijnlijk wel eens behulpzaam kunnen zijn. Dus laten we er een schrijven. We kunnen een matcher toevoegen in een van beide BeforeEach bel of een het bel (nou, ik denk het wel kon doe het in een Na elke bel maar dat zou niet zo logisch zijn). Zo begin je:

beforeEach (function () this.addMatchers (););

Best simpel, toch? Wij bellen this.addMatchers, het doorgeven van een objectparameter. Elke sleutel in dit object wordt de naam van een overeenkomende partij en de bijbehorende functie (de waarde) is de manier waarop deze wordt uitgevoerd. Laten we zeggen dat we een matcher willen maken die met controle controleert of een getal tussen twee andere ligt. Dit is wat je zou schrijven:

beforeEach (function () this.addMatchers (toBe Between: function (rangeFloor, rangeCeiling) if (rangeFloor> rangeCeiling) var temp = rangeFloor; rangeFloor = rangeCeiling; rangeCeiling = temp; retourneer this.actual> rangeFloor && this. werkelijk < rangeCeiling;  ); );

We nemen eenvoudigweg twee parameters, zorgen ervoor dat de eerste kleiner is dan de tweede en geven een boolean-instructie terug die evalueert naar waar als aan onze voorwaarden is voldaan. Het belangrijkste om hier op te merken is hoe we de waarde die is doorgegeven aan de verwachten functie: this.actual.

it ("is between 5 and 30", function () expect (10) .toBeween (5, 30);); it ("is tussen 30 en 500", functie () verwacht (100) .toBeween (500, 30););

Dit is wat de SpecHelper.js bestand doet; het heeft een beforeEach oproep die de matcher toevoegt tobePlaying (). Bekijken!


Conclusie: plezier hebben!

Met Jasmine kun je veel meer doen: functiegerelateerde matchers, spionnen, asynchrone specificaties en meer. Ik raad aan dat je de wiki verkent als je geïnteresseerd bent. Er zijn ook een aantal bijbehorende bibliotheken die het testen in de DOM eenvoudiger maken: Jasmine-jQuery en Jasmine-fixture (afhankelijk van Jasmine-jQuery).

Dus als u tot nu toe uw JavaScript niet test, is dit een uitstekend moment om te beginnen. Zoals we hebben gezien, maakt de snelle en eenvoudige syntax van Jasmine testen behoorlijk eenvoudig. Er is gewoon geen reden voor je om het niet te doen. Nu, is er?

Als u uw JavaScript-ontwikkeling verder wilt zetten, waarom zou u dan niet het bereik van JavaScript-items op Envato Market bekijken? Er zijn duizenden scripts, apps en codefragmenten om u te helpen.