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.
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:
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:
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!
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.
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.
convert.js
in de src
map.convertSpec.js
in de spec
map,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.
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:
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.fromUnit
naar de juiste sleutel.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.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.
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.
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 (););
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 (););
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););
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 /););
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.
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.
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!
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.