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.
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.
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
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
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
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".
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)
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.
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.
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.
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
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.
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
.
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.
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.
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.
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.
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.
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-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
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)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:
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 ä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:
.travis.yml
filen i rotkatalogen i ditt projekt och binda det till ditt GitHub-arkivDet 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 fordra
om 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-.
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.