Testa din Ruby Code med Guard, RSpec & Pry Del 2

Välkommen tillbaka! Om du missade den första delen av vår resa hittills kanske du vill gå tillbaka och fånga upp först.

Hittills har vi tillämpat en testdriven utvecklingsprocess för att bygga vår applikation, tillsammans med den populära RSpec-testramen. Härifrån kommer vi att undersöka några andra RSpec-funktioner och se till att använda Pry-pärlan för att hjälpa dig att felsöka och skriva din kod.

Andra RSpec-funktioner

Låt oss ta en stund att granska några andra RSpec-funktioner som vi inte behöver i det här enkla exemplet, men du kan hitta användbar bearbetning på ditt eget projekt.

Väntar på uttalande

Föreställ dig att du skriver ett test men du är avbruten, eller du måste lämna ett möte och inte har slutfört koden som krävs för att provet ska passera.

Du kan ta bort testet och skriva om det senare när du kan komma tillbaka till ditt arbete. Eller alternativt kan du bara kommentera koden, men det är ganska fult och definitivt inte bra när du använder ett versionsstyrningssystem.

Det bästa att göra i denna situation är att definiera vårt test som "väntar" så när testen körs, kommer testramen att ignorera testet. För att göra detta måste du använda avvaktan nyckelord:

beskriv "någon metod" gör det "borde göra någonting" i väntan på slutet 

Set-up och Tear-Down

Alla bra testramar tillåter dig att köra kod före och efter varje test körs. RSpec är inte annorlunda.

Det ger oss innan och efter metoder som gör det möjligt för oss att sätta upp ett specifikt tillstånd för att vårt test ska springa och sedan städa upp det tillståndet efter det att testet har kört (det här är så att staten inte läcker ut och påverkar resultatet av efterföljande test).

beskriva "någon metod" gör före (: varje) gör # någon uppställningskod slut efter (: varje) gör # någon tårar ner kod sluta "borde göra någonting" väntande ändänden 

Context Blocks

Vi har redan sett beskriva blockera; men det finns ett annat block som kallas funktionellt ekvivalent sammanhang. Du kan använda den varhelst du skulle använda beskriva.

Skillnaden mellan dem är subtil men viktig: sammanhang tillåter oss att definiera ett tillstånd för vårt test. Inte uttryckligen dock (vi ställer faktiskt inte staten genom att definiera a sammanhang blockera - det är i stället för läsbarhetsändamål så att avsikten med följande kod är tydligare).

Här är ett exempel:

beskriva "Några metoder" gör kontext "block tillhandahållet" gör det "ger för att blockera" väntande slutändametekonvertering "inget block som tillhandahålls" gör det "kallar en återgångsmetod" väntande ändändsänden 

stubbar

Vi kan använda stump metod för att skapa en falsk version av ett befintligt objekt och att få det att returnera ett förutbestämt värde.

Det här är användbart när det gäller att förhindra att våra test berör API för direkttjänst och guider våra test genom att ge förutsägbara resultat från vissa samtal.

Föreställ dig att vi har en klass kallad Person och att den här klassen har en tala metod. Vi vill testa att metoden fungerar hur vi förväntar oss det. För att göra detta stubbar vi tala metod med följande kod:

beskriv person gör det "talar" gör bob = stub () bob.stub (: tal) .och_return ("hej") Person.any_instance.stub (: initialisera) .and_return (bob) instance = Person.new expect example.speak). till eq ("hej") änden 

I det här exemplet säger vi att "någon förekomst" av Person klassen ska ha sin initialisera Metoden stubbar så att den returnerar objektet guppa.

Du märker det guppa är en stub som är uppbyggd så att någon tidskod försöker utföra tala metod kommer det att återvända "hej".

Vi fortsätter sedan för att skapa en ny Person instans och vidarebefordra samtalet till instance.speak in i RSpec s förvänta syntax.

Vi berättar för RSpec att vi förväntar oss att samtalet resulterar i strängen "hej".

Efterföljande returvärden

I de tidigare exemplen använder vi RSpec-funktionen och återvänd för att ange vad vår stub ska returnera när den heter.

Vi kan ange ett annat returvärde varje gång stubben heter genom att ange flera argument till och återvänd metod:

obj.stub (: foo) .and_return (1, 2, 3) förvänta (obj.foo ()). till eq (1) förvänta (obj.foo ()). till eq (2) förvänta (obj.foo ()). till eq (3) 

hånar

Mocks ligner Stubs genom att vi skapar falska versioner av våra föremål men istället för att returnera ett fördefinierat värde styrs vi mer specifikt vägarna våra objekt måste ta för provet att vara giltigt.

För att göra det använder vi falsk metod:

beskriv Obj gör det "testning" gör bob = mock () bob.should_receive (: testning) .with ('content') Obj.any_instance.stub (: initialisera) .and_return (bob) instance = Obj.new instans. testning ("något värde") änden 

I ovanstående exempel skapar vi en ny Objekt exempel och ring sedan dess testning metod.

Bakom kulisserna i den koden förväntar vi oss testning metod som ska kallas med värdet 'innehåll'. Om det inte kallas med det värdet (vilket i ovanstående exempel det inte är) vet vi att en del av vår kod inte har fungerat ordentligt.

Ämnesblock

De ämne sökord kan användas på ett par olika sätt. Alla är utformade för att minska koddubbling.

Du kan använda det implicit (märka vårt Det block refererar inte till ämne alls):

beskriv Array beskriver "med 3 objekt" gör ämnet [1,2,3] det should_not be_empty änden 

Du kan använda det explicit (märka vårt Det block hänvisar till ämne direkt):

beskriv MyClass gör beskriva "initialisering" gör ämne MyClass det "skapar en ny instans" gör instance = subject.new expect (instance) .to be_a (MyClass) slutet änden 

Snarare än att ständigt referera till ett ämne inom din kod och passera i olika värden för instantiering, till exempel:

beskriv "Foo" gör kontext "A" gör det "Bar" gör baz = Baz.new ('a') förvänta (baz.type) .till eq ('a') ändändamellekontext "B" gör det "Bar" gör baz = Baz.new ('b') förvänta (baz.type) .till eq ('b') änden sammanhang "C" gör det "Bar" do baz = Baz.new ('c') förvänta .type). till eq ('c') ändänden 

Du kan istället använda ämne tillsammans med låta för att minska dubbelarbete:

beskriv "Person" gör ämne Person.new (namn) # Person har ett get_name-metodkontext "Bob" laddar (: namn) 'Bob' dess (: get_name) should == 'Bob' slutet sammanhang "Joe" laddar (: namn) 'Joe' dess (: get_name) should == 'Joe' slutet kontext "Smith" == 'Smith' slutet slutet 

Det finns många fler funktioner tillgängliga för RSpec men vi har tittat på de viktigaste som du kommer att hitta dig själv med mycket när du skriver test med RSpec.

Slumpmässiga test

Du kan konfigurera RSpec för att köra dina tester i slumpmässig ordning. Detta gör att du kan se till att ingen av dina tester har något beroende eller beroende av de andra testerna kring det.

RSpec.configure do | config | config.order = "random" slutet 

Du kan ställa in det här via kommandot med hjälp av --ordning flagga / alternativet. Till exempel: rspec - ordning slumpmässig.

När du använder --ordning slumpmässig alternativet RSpec kommer att visa det slumptal som det använde för att fröpa algoritmen. Du kan använda detta "frö" -värde igen när du tror att du har upptäckt ett beroendeproblem inom dina test. När du har bestämt vad du tycker är problemet kan du skicka frövärdet till RSpec (t ex om fröet var 1234 kör sedan --ordning slumpmässig: 1234) och det kommer att använda samma slumpmässiga frö för att se om det kan replikera det ursprungliga beroendet.

Global konfiguration

Du har sett att vi har lagt till en projektspecifik uppsättning konfigurationsobjekt inom vår Rakefile. Men du kan ställa in konfigurationsalternativ globalt genom att lägga dem till en .rspec filen i din hemkatalog.

Till exempel inuti .rspec:

--färg - format nestad 

Debugging With Pry

Nu är vi redo att börja titta på hur vi kan felsöka vår ansökan och vår testkod med Pry-pärlan.

Det är viktigt att förstå att även om Pry är riktigt bra för att felsöka din kod, är det faktiskt tänkt som ett förbättrat Ruby REPL-verktyg (att ersätta irb) och inte strikt felsökningsändamål så till exempel finns inga inbyggda funktioner som: gå in i, gå över eller steg ut etc som du normalt skulle hitta i ett verktyg som är utformat för debugging.

Men som ett felsökningsverktyg är Pry mycket fokuserad och mager.

Vi kommer tillbaka till felsökning på ett ögonblick, men låt oss först granska hur vi använder Pry i början.

Uppdaterat kod exempel

För att visa Pry ska jag lägga till mer kod till mitt exemplarprogram (denna extra kod påverkar inte vårt test på något sätt)

klass RSpecGreeter attr_accessor: test @@ class_property = "Jag är en klassegenskap" def greet binding.pry @instance_property = "Jag är en instans egendom" pubar privs "Hej RSpec!" end def pubs test_var = "Jag är en testvariabel" test_var ände privat def privs sätter "Jag är privat" änden 

Du märker att vi har lagt till några extra metoder, exempel och klassegenskaper. Vi ringer också till två av de nya metoderna som vi har lagt till från vår hälsa metod.

Slutligen märker du användningen av binding.pry.

Ställ in brytpunkter med binding.pry

En brytpunkt är en plats i din kod där körningen kommer att sluta.

Du kan ha flera brytpunkter inställda inom din kod och du skapar dem med binding.pry.

När du kör din kod märker du att terminalen kommer att sluta och placera dig inuti din ansökans kod på exakt platsen din binding.pry placerades.

Nedan är ett exempel på hur det kan se ut ...

 8: def greet => 9: binding.pry 10: pubar 11: privs 12: "Hej RSpec!" 13: slutet 

Från den här tiden har Pry tillgång till det lokala räckhållet så att du kan använda Pry som du vill irb och börja skriva in till exempel variabler för att se vilka värden de har.

Du kan köra utgång kommandot för att avsluta Pry och för din kod för att fortsätta exekvera.

Hitta var du är: var är jag

När du använder mycket av binding.pry brytpunkter kan det vara svårt att förstå var i ansökan du är.

För att få ett bättre sammanhang av var du än befinner dig kan du använda var är jag kommando.

När du kör på egen hand ser du något som liknar när du använde binding.pry (du ser linjen där brytpunkten var inställd och ett par rader över och under det). Skillnaden är att om du passerar ett extra numeriskt argument som whereami 5 Du får se fem extra rader ovanför där binding.pry var placerad. Du kan begära att se 100 linjer runt den aktuella brytpunkten till exempel.

Det här kommandot kan hjälpa dig att orientera dig inom den aktuella filen.

Stack Trace: wtf

De wtf kommandot står för "vad f ***" och det ger en full stack spår för det senaste undantaget som har kastats. Det kan hjälpa dig att förstå stegen som leder till felet som uppstod.

inspektera: ls

De ls Kommando visar vilka metoder och egenskaper som finns tillgängliga för Pry.

När det körs kommer det att visa dig något som ...

RSpecGreeter # metoder: hälsar pubar testtest = klassvariabler: @@ class_property lokalbefolkningen: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ 

I ovanstående exempel kan vi se att vi har fyra offentliga metoder (kom ihåg att vi uppdaterade vår kod för att inkludera några ytterligare metoder och sedan testa och test = skapades när man använde Ruby's attr_accessor kort hand).

Det visar också andra klass- och lokala variabler som Pry kan komma åt.

En annan användbar sak du kan göra är att greppa (söka) resultaten för det du bara är intresserad av. Du behöver förståelse för Regular Expressions, men det kan vara en praktisk teknik. Här är ett exempel…

ls -p-G ^ p => RSpecGreeter # metoder: privs 

I ovanstående exempel använder vi -p och -G alternativ / flaggor som berättar Pry vi vill bara se offentliga och privata metoder och vi använder regex ^ p (vilket betyder att matcha allt som börjar med p) som vårt sökmönster för att filtrera resultaten.

Löpning ls --hjälp kommer också att visa dig alla tillgängliga alternativ.

Ändra omfattning: CD

Du kan ändra det aktuella räckviddet med hjälp av CD kommando.

I vårt exempel om vi kör cd ... / pubar det tar oss till resultatet av det metodsamtalet.

Om vi ​​nu kör var är jag du ser att den kommer att visas Inne i "Jag är en testvariabel".

Om vi ​​kör själv då får vi se "Jag är en testvariabel" returnerad.

Om vi ​​kör self.class vi får se Sträng returnerad.

Du kan flytta upp kedjan med hjälp av CD… eller du kan gå tillbaka till den översta nivån av räckvidden med CD /.

Obs: vi kunde lägga till en annan binding.pry inuti pubar metod och då skulle vårt räckvidd vara inuti den metoden snarare än resultatet av metoden.

Se hur djupt du är: nesting

Tänk på föregående exempel på körning cd pubar. Om vi ​​kör nesting kommandot får vi en överblick över antalet kontekster / nivåer som Pry har för närvarande:

Nestestatus: - 0. # (Pry toppnivå) 1. "Jag är en testvariabel" 

Därifrån kan vi springa utgång att flytta tillbaka till det tidigare sammanhanget (till exempel inuti hälsa metod)

Löpning utgång igen betyder att vi stänger det sista sammanhanget Pry har och så Pry avslutar och vår kod fortsätter att springa.

Hitta någon metod: hitta-metoden

Om du inte är säker på var du ska hitta en viss metod kan du använda hitta-metoden Kommando att visa alla filer i din kodbas som har en metod som matchar det du söker efter:

find-method priv => Kärnkärnan # private_methods Modulmodul # private_instance_methods Modul # private_constant Modul # private_method_defined? Modul # private_class_method Modul # privat RSpecGreeter RSpecGreeter # privs 

Du kan också använda -c alternativ / flagga för att söka innehållet i filer istället:

find-method -c greet => RSpecGreeter RSpecGreeter: def greet RSpecGreeter # privs: hälsning 

Classic Debugging: Nästa, steg, Fortsätta

Även om ovanstående tekniker är användbara, är det inte riktigt "felsökning" på samma sätt som vad du förmodligen brukar använda.

För de flesta utvecklare ger deras redaktör eller webbläsare dem ett inbyggt felsökningsverktyg som låter dem faktiskt gå igenom sin kodlinje för rad och följa den väg som koden tar till dess att den är klar.

Som Pry är utvecklad för att användas som ett REPL som inte är att säga att det inte är användbart för debugging.

En naiv lösning skulle vara att ställa in flera binding.pry uttalanden genom en metod och användning ctrl-d att röra sig genom varje brytpunktsuppsättning. Men det är fortfarande inte riktigt bra nog.

För stegvisa felsökning kan du ladda pärlemörken ...

källa "https://rubygems.org" gem 'rspec "grupp: utveckling gör gem" guard "pärla" guard-rspec "pärla" pry "# Lägger felsökningssteg till Pry # fortsätt, steg, nästa pärla" pry-remote " pärla "pry-nav" -änden 

Denna pärla sträcker sig Pry så det förstår följande kommandon:

  • Nästa (gå till nästa rad)
  • Steg (flytta till nästa rad och om det är en metod, gå sedan in i den metoden)
  • Fortsätta (Ignorera ytterligare brytpunkter i den här filen)

Kontinuerlig integration med Travis-CI

Som en extra bonus låt oss integrera våra tester med online-tjänsten CI (kontinuerlig integration) Travis-CI.

Principen för CI är att begå / trycka tidigt och ofta för att undvika konflikter mellan din kod och huvudgrenen. När du gör (i det här fallet begår vi GitHub), då bör det sparka upp en "build" på din CI-server som driver relevanta tester för att säkerställa att allt fungerar som det ska vara.

Nu med TDD som din primära utvecklingsmetodik är du mindre benägna att drabbas av fel varje gång du trycker på att dina test är en integrerad del av ditt utvecklingsarbete och så innan du trycker på du kommer du redan att vara medveten om buggar eller regressioner. Men det skyddar dig inte nödvändigtvis från fel som inträffar från integrationsprov (där all kod över flera system körs tillsammans för att säkerställa att systemet som helhet fungerar korrekt).

Oavsett, kod bör aldrig drivas direkt till din liveproduktionsservern på något sätt; det bör alltid skjutas först till en CI-server för att fånga eventuella buggar som uppstår vid skillnader mellan din utvecklingsmiljö och produktionsmiljön.

Många företag har ännu fler miljöer för att deras kod ska gå igenom innan den når liveproduktionsservern.

Till exempel på BBC News har vi:

  • CI
  • Testa
  • Skede
  • leva

Trots att varje miljö borde vara identisk i inställningen är syftet att genomföra olika typer av test för att säkerställa att många buggar fångas och lösas innan koden når "live".

Travis-CI

Travis CI är en värd för kontinuerlig integrationstjänst för open source-gemenskapen. Den är integrerad med GitHub och erbjuder förstklassigt stöd för flera språk

Vad detta innebär är att Travis-CI erbjuder gratis CI-tjänster för open source-projekt och har också en betald modell för företag och organisationer som vill behålla sin CI-integration privat.

Vi använder den fria open source-modellen på vårt exempel GitHub-arkiv.

Processen är detta:

  • Registrera ett konto hos GitHub
  • Logga in på Travis-CI med ditt GitHub-konto
  • Gå till sidan "Konton"
  • Slå på "på" alla arkiv som du vill köra CI på
  • Skapa en .travis.yml filen i rotkatalogen i ditt projekt och binda det till ditt GitHub-arkiv

Det sista steget är det viktigaste (skapa en .travis.yml fil) eftersom det här bestämmer konfigurationsinställningarna för Travis-CI så det vet hur man hanterar körning av testen för ditt projekt.

Låt oss ta en titt på .travis.yml fil som vi använder för vårt exempel GitHub repository:

språk: ruby ​​cache: bundler rvm: - 2.0.0 - 1.9.3 script: 'bundle exec rake spec' bundlar_args: - utan utvecklingsgrenar: endast: - huvudmeddelanden: email: - [email protected] 

Låt oss bryta ner det här stycket för bit ...

Först specificerar vi vilket språk vi använder i vårt projekt. I det här fallet använder vi Ruby: språk: rubin.

För att springa Bundler kan vara lite långsam och vi vet att våra beroende inte kommer att förändras så ofta kan vi välja att cache beroendet, så vi sätter cache: bundlare.

Travis-CI använder RVM (Ruby Version Manager) för att installera Rubies på sina servrar. Så vi måste ange vilka Ruby versioner vi vill köra våra tester mot. I det här fallet har vi valt 2,0 och 1.9.3 vilka är två populära Ruby-versioner (tekniskt använder vår applikation Ruby 2 men det är bra att veta våra kodpass i andra versioner av Ruby också):

rvm: - 2,0,0 - 1,9,3 

För att köra våra tester vet vi att vi kan använda kommandot räfsa eller rake spec. Travis-CI kör som standard kommandot räfsa men på grund av hur Gems är installerade på Travis-CI med Bundler måste vi ändra standardkommandot: script: "bunt exec rake spec". Om vi ​​inte gjorde det skulle Travis-CI ha ett problem att lokalisera rspec / kärna / rake_task fil som anges i vår Rakefile.

Obs! Om du har några problem med Travis-CI kan du gå med i #travis-kanalen på IRC freenode för att få hjälp att svara på eventuella frågor du kan ha. Det var där jag upptäckte lösningen på min fråga med Travis-CI att inte kunna köra mina test med standard räfsa kommandot och förslaget att skriva över standard med bunt exec rake löst det problemet.

Eftersom vi bara är intresserade av att köra våra tester kan vi vidarebefordra ytterligare argument till Travis-CI för att filtrera ädelstenar som vi inte vill störa installeringen. Så för oss vill vi utesluta att installera ädelstenar grupperade som utveckling: bundlar_args: - utan utveckling (det betyder att vi exkluderar ädelstenar som bara används verkligen för utveckling och felsökning som Pry och Guard).

Det är viktigt att notera att jag ursprungligen laddar Pry inom vårt spec_helper.rb fil. Detta orsakade ett problem när koden kördes på Travis-CI, nu när jag utesluter "utveckling" -mycken. Så jag var tvungen att tweak koden så här:

kräva "pry" om ENV ['APP_ENV'] == 'debug' 

Du kan se att nu är Pry-pärlan bara fordraom en miljövariabel för APP_ENV är inställd på felsökning. På så sätt kan vi undvika att Travis-CI kastar några fel. Det betyder att när du kör din kod lokalt måste du ange miljövariabeln om du vill felsöka din kod med Pry. Nedan visas hur detta kan göras i en rad:

APP_ENV = debug && ruby ​​lib / example.rb

Det var två andra ändringar jag gjorde och det var för vår Gemfile. En var att klargöra vilka pärlor som krävdes för testning och vilka som krävdes för utveckling, och den andra var uttryckligen nödvändig av Travis-CI:

källa "https://rubygems.org" grupp: test göra pärla "rake" pärla "rspec" slutgrupp: utveckling gör pärla "vakt" pärla "guard-rspec" pärla "pry" # Lägger felsökningssteg till Pry # fortsätt, steg, nästa pärla "pry-remote" pärla "pry-nav" slutet 

Titta på ovanstående uppdaterade Gemfile vi kan se att vi har flyttat RSpec-pärlan till en ny testa grupp, så nu borde det vara tydligare vilket syfte varje pärla har. Vi har också lagt till en ny pärla "rake". Travis-C-dokumentationen säger att detta måste anges explicit.

Nästa avsnitt är valfritt och det tillåter dig att använda en vit lista (eller svart lista) vissa grenar i ditt förråd. Så som standard kommer Travis-CI att köra tester mot alla dina filialer om du inte säger det annars. I det här exemplet säger vi att vi bara vill att det ska springa mot vårt bemästra gren:

grenar: endast: - mästare 

Vi skulle kunna berätta att vi kör varje gren "förutom" en viss filial, som så:

grenar: förutom: - some_branch_I_dont_want_run 

Den sista sektionen berättar Travis-CI var att skicka meddelanden till när en byggnad misslyckas eller lyckas:

anmälningar: email: - [email protected] 

Du kan ange flera e-postadresser om du vill:

anmälningar: email: - [email protected] - [email protected] - [email protected] 

Du kan vara mer specifik och ange vad du vill hända på antingen ett misslyckande eller en framgång (till exempel fler personer kommer bara att vara intresserade om testen misslyckas snarare än att få ett mail varje gång de passerar):

anmälningar: email: mottagare: - [email protected] on_failure: ändra on_success: aldrig 

Ovanstående exempel visar att mottagaren aldrig kommer att få ett e-postmeddelande om testen passerar men kommer att få meddelande om felstatus ändras (standardvärdet för båda är alltid vilket innebär att du alltid kommer att bli underrättad oavsett vad statusen resulterar i).

Obs! När du explicit anger a on_failure eller on_success Du måste flytta e-postadressen (erna) inuti mottagare nyckel-.

Slutsats

Detta är slutet på vår tvådel titta på RSpec, TDD och Pry.

I del ett lyckades vi skriva vår ansökan med hjälp av TDD-processen och RSpec-testramen. Under den andra halvan har vi också utnyttjat Pry för att visa hur vi lättare kan felsöka en löpande Ruby-applikation. Slutligen kunde vi få våra testinställningar att köras som en del av en kontinuerlig integrationsserver med den populära Travis-CI-tjänsten.

Förhoppningsvis har det givit dig tillräckligt med smak så att du är angelägen om att undersöka var och en av dessa tekniker vidare.