Ruby for Newbies Test med Rspec

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.


Föredrar en skärmdump?

Ser bekant ut?

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!


Inställning

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!


De bok Klass

Lå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!


Specificerar ut Bibliotek klass

Den 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:


Några senaste matchare

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.

Vill du lära dig mer?

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.