Ruby är ett av de mest populära språk som används på webben. Vi kör en session här på Nettuts + som kommer att introducera dig till Ruby, liksom de stora ramar och verktyg som följer med Ruby utveckling. I det här avsnittet lär du dig att testa din Ruby-kod med Rspec, ett av de bästa testbiblioteken i verksamheten.
Om du har läst min senaste handledning på JasmineJS kommer du förmodligen att märka flera likheter i Rspec. Egentligen är likheterna i Jasmine: Jasmine skapades med Rspec i åtanke. Vi ska titta på hur man kan använda Rspec för att göra TDD i Ruby. I den här handledningen skapar vi några utrustade Ruby-klasser för att bekanta oss med Rspec-syntaxen. Men nästa? Ruby för nybörjare? episod kommer att fungera med hjälp av Rspec i samband med några andra bibliotek för att testa webapps? så håll dig stillad!
Det är ganska enkelt att installera Rspec. Pop öppna den här kommandoraden och kör följande:
gem installation rspec
Så lätt.
Nu, låt oss sätta upp ett litet projekt. Vi ska skapa två klasser: bok
och Bibliotek
. Vår bok
objekt kommer bara att lagra en titel, författare och kategori. Vår Bibliotek
objektet lagrar en lista med böcker, sparar dem till en fil och tillåter oss att hämta dem per kategori.
Här är vad din projektkatalog ska se ut:
Vi lägger specifikationerna (eller specifikationerna) i en spec
mapp; Vi har en spec fil för varje klass. Lägg märke till spec_helper.rb
fil. För våra specs att springa måste vi fordra
Ruby klasserna vi testar. Det är vad vi gör inne i spec_helper
fil:
require_relative '? / biblioteket "require_relative"? / boken 'kräver' yaml '
(Har du träffat require_relative
än? Nej? Väl, require_relative
är precis som fordra
, förutom att istället för att söka din Ruby-sökväg söker den släkting till den aktuella katalogen.)
Du kanske inte är bekant med YAML-modulen; YAML är en enkel textdatabas som vi ska använda för att lagra data. Du får se hur det fungerar, och vi pratar mer om det senare.
Så, nu när vi är uppbyggda, låt oss få spricka på vissa specifikationer!
bok
KlassLåt oss börja med testen för bok
klass.
kräva "spec_helper" beskriva boken slutar
Så här börjar vi: med a beskriva
blockera. Vår parameter till beskriva
förklarar vad vi testar: det här kan vara en sträng, men i vårt fall använder vi klassnamnet.
Så vad ska vi lägga in här beskriva
blockera?
före: var och en @book = Book.new "Titel", "Författare":: Kategori slut
Vi börjar med att ringa till innan
; vi passerar symbolen :varje
att ange att vi vill att den här koden ska köras före varje test (vi kan också göra det :Allt
att köra den en gång före alla tester). Vad gör vi exakt före varje test? Vi skapar en förekomst av bok
. Lägg märke till hur vi gör det till en instansvariabel genom att lägga ut variabelnamnet med @
. Vi behöver göra så att vår variabel kommer att vara tillgänglig från våra test. Annars får vi bara en lokal variabel som bara är bra inuti innan
blockera? vilket inte alls är bra.
Gå vidare,
beskriv "#ny" gör det "tar tre parametrar och returnerar ett bokobjekt" gör @ book.should be_an_instance_of Bokslutsänden
Här är vårt första test. Vi använder en kapslad beskriva
blockera här för att säga att vi beskriver åtgärderna för en viss metod. Du kommer märka att jag har använt strängen? # Ny? det är en konvention i Ruby att prata hänvisa till exempel metoder så här: Classname # method
Eftersom vi har klassnamnet i vår toppnivå beskriva
, Vi lägger bara metodnamnet här.
Vårt test bekräftar helt enkelt att vi faktiskt har gjort ett bokobjekt.
Observera grammatiken vi använder här: object.should do something
. Nittiofem procent av dina tester kommer att ta denna form: du har ett föremål, och du börjar ringa skall
eller borde inte
på objektet. Då skickar du till det objektet ett samtal till en annan funktion. I det här fallet är det be_an_instance_of
(som tar bok
som sin enda parameter). Sammantaget gör detta ett perfekt läsbart test. Det är mycket tydligt att @bok
bör vara en förekomst av klassen bok
. Så, låt oss köra den.
Öppna din terminal, CD
in i projektkatalogen och kör rspec spec
. De spec
är mappen i vilken rspec
kommer att hitta testen. Du ska se produktionen som säger något om? Uninitialized Constant Object :: Book ?; det här betyder bara att det inte finns något bok
klass. Låt oss fixa det.
Enligt TDD vill vi bara skriva tillräckligt med kod för att åtgärda detta problem. I book.rb
fil, det skulle vara så här:
bokens bokslut
Kör testet igen (rspec spec
), och du finner det går bra. Vi har inte en initialisera
metod, så kallar Ruby # nya
har ingen effekt just nu. Men vi kan skapa bok
Objekt (om än ihåliga). Normalt skulle vi följa den här processen genom resten av vår utveckling: Skriv ett test (eller några relaterade tester), se det misslyckas, få det att passera, refactor, repeat. Men för den här handledningen visar jag bara testerna och koden, och vi diskuterar dem.
Så, fler tester för bok
:
beskriv "#title" gör det "returnerar den rätta titeln" do @ book.title.should eql "Titel" slutänden beskriver "#author" gör det "returnerar rätt författare" gör @ book.author.should eql "Author" slutänden beskriver "#category" do it "returnerar den korrekta kategorin" do @ book.category.should eql: category end end
Det borde vara ganska framträdande för dig. Men märk hur vi jämför i provet: med eql
. Det finns tre sätt att testa för jämlikhet med Rspec: med användaren ==
eller metoden eql
båda tillbaka Sann
om de två objekten har samma innehåll. Till exempel är båda strängar eller symboler som säger samma sak. Då finns det lika
, som bara returnerar sant i de två objekten är verkligen och sannolikt lika, vilket betyder att de är samma föremål i minnet. I vårat fall, eql
(eller ==
) är vad vi vill ha.
Dessa kommer att misslyckas, så här är koden för bok
för att få dem att gå:
klassbok Attr_accessor: titel,: författare:: kategori def initiera titel, författare, kategori @title = title @author = author @category = endänd
Låt oss gå vidare till Bibliotek
!
Bibliotek
klassDen här blir lite mer komplicerad. Låt oss börja med detta:
behöver "spec_helper" beskriva "Library Object" gör före: alla gör lib_obj = [Book.new ("JavaScript: The Good Parts", "Douglas Crockford",: utveckling), Book.new ("Designing with Web Standards" Jeffrey Zeldman ",: design), Book.new (" Gör mig inte tänk "," Steve Krug ",: användbarhet), Book.new (" JavaScript Patterns "," Stoyan Stefanov ",: utveckling), Book. ny ("Responsive Web Design", "Ethan Marcotte",: design)] File.open "books.yml", "w" gör | f | f.write YAML :: dump lib_obj slutet slutet innan: var och en gör @lib = Library.new "books.yml" änden
Det här är all uppställning: vi använder två innan
block: en för :varje
och en för :Allt
. I före: alla
block, skapar vi en rad böcker. Då öppnar vi filen? Books.yml? (i "writer" -läget) och användning YAML
att dumpa arrayen i filen.
Kort kaninspår för att förklara YAML lite bättre: YAML är enligt webbplatsen en mänsklig vänlig serialiseringsstandard för alla programmeringsspråk.? Det är som en textbaserad databas, ganska som JSON. Vi importerar YAML i vår spec_helper.rb
. De YAML
modulen har två huvudmetoder du ska använda: dumpa
, som matar ut serialiserade data som en sträng. Sedan, ladda
tar datasträngen och täcker den tillbaka till Ruby-objekt.
Så, vi har skapat den här filen med viss data. Innan :varje
test, vi ska skapa en Bibliotek
objekt, skickar det namnet på YAML-filen. Nu får vi se testen:
beskriva "#ny" gör sammanhang "utan några parametrar" gör det "har inga böcker" do lib = Library.new lib.should have (0) .böcker avslutar sammanhang "med en yaml-filparameter" gör det "har fem böcker "do @ lib.should have (5) .books avslutar slutet" returnerar alla böcker i en given kategori "do @ lib.get_books_in_category (: development) .length.should == 2 avsluta det" accepterar nya böcker " @ lib.add_book (Book.new ("Designing for the Web", "Mark Boulton",: design)) @ lib.get_book ("Designing for the Web") bör vara_an_instans_av bokslut det "sparar biblioteket" = @ lib.books.map | book | book.title @ lib.save lib2 = Library.new "books.yml" books2 = lib2.books.map | bok | book.title books.should eql books2 avsluta
Vi börjar med ett inre beskriva
blockera speciellt för Library # nytt
metod. Vi introducerar ett annat block här: sammanhang
Detta gör det möjligt för oss att ange ett sammanhang för test inuti det, eller ange olika resultat för olika situationer. I vårt exempel har vi två olika sammanhang:? Utan parametrar? och? med en yaml-filparameter ?; dessa visar de två beteenden för användning Library # nytt
.
Observera också de testmatchare vi använder i dessa två test: lib.should have (0) .books
och @ lib.should have (5) .books
. Det andra sättet att skriva detta skulle vara lib.books.length.should == 5
, men det här är inte så läsligt. Det visar emellertid att vi behöver ha en böcker
egendom som är en uppsättning av de böcker vi har.
Sedan har vi tre andra tester för att testa funktionaliteten att få böcker per kategori, lägga till en bok i biblioteket och spara biblioteket. Dessa misslyckas, så låt oss skriva klassen nu.
klass Bibliotek attr_accessor: böcker def initiera lib_file = false @lib_file = lib_file @books = @lib_file? YAML :: load (File.read (@lib_file)): [] slut def get_books_in_category category @books.select do | book | book.category == kategori slutänden def add_book bok @ books.push bok slutet def get_book title @ books.select do | book | book.title == title end.first slutet def save lib_file = false @lib_file = lib_file || @lib_file || "library.yml" File.open @lib_file, "w" gör | f | f.write YAML :: dump @books slutet slutet
Vi kunde skriva upp fler tester och lägga till mycket annan funktionalitet till detta Bibliotek
klass, men vi kommer att stanna där. Nu körs rspec spec
, Du kommer att se att alla tester passerar.
Det ger dock inte så mycket information om testen. Om du vill se mer, använd den indelade formatparametern: rspec spec - format nestad
. Du får se detta:
Innan vi lägger upp, låt mig visa dig ett par andra matchare
obj.should be_true
, obj.should be_false
, obj.should be_nil
, obj.should be_empty
- De tre första av dessa kunde göras av == true
, etc. be_empty
kommer att vara sant om obj.empty?
är sant. obj.should exist
- finns det här objektet ännu än?obj.should have_at_most (n) .items
, object.should have_at_least (n) .items
- tycka om ha
, men kommer att passera om det finns fler eller färre än n
artiklar, respektive.obj.should include (a [, b,?])
- är ett eller flera objekt i en array? obj.should match (string_or_regex)
- matchar objektet strängen eller regexen?obj.should raise_exception (error)
- Upprepar denna metod ett fel när det kallas?obj.should respond_to (metodnamn)
- har det här objektet den här metoden? Kan ta mer än ett metodnamn, i antingen strängar eller symboler.Rspec är en av de bästa ramarna för testning i Ruby, och det finns ett ton du kan göra med det. För att lära dig mer, kolla in Rspec-webbplatsen. Det finns också The Rspec-boken, som lär mer än bara Rspec: det handlar om TDD och BDD i Ruby. Jag läser det nu, och det är extremt grundligt och djupt.
Tja, det är allt för den här lektionen! Nästa gång tittar vi på hur vi kan använda Rspec för att testa gränssnitten i en webapp.