Velkommen tilbake! Hvis du savnet den første delen av reisen så langt, vil du kanskje gå tilbake og ta opp først.
Så langt har vi brukt en testdrevet utviklingsprosess for å bygge applikasjonen vår, sammen med å bruke det populære RSpec testrammen. Herfra skal vi undersøke noen andre RSpec-funksjoner, samt se på bruk av Pry-perlen for å hjelpe deg med å feilsøke og skrive koden din.
La oss ta et øyeblikk til å gjennomgå noen andre RSpec-funksjoner som vi ikke har trengte i dette enkle eksempelprogrammet, men du kan finne nyttig arbeid på ditt eget prosjekt.
Tenk deg at du skriver en test, men du blir avbrutt, eller du må gå for et møte og ikke har fullført koden som kreves for å få testen til å passere.
Du kan slette testen og skrive den på nytt senere når du kan komme tilbake til arbeidet ditt. Eller alternativt kan du bare kommentere koden, men det er ganske stygg og definitivt ikke bra når du bruker et versjonskontrollsystem.
Det beste å gjøre i denne situasjonen er å definere vår test som "venter" så når testene kjøres, vil testrammen ignorere testen. For å gjøre dette må du bruke i påvente av
søkeord:
Beskriv "noen metode" gjør det "skal gjøre noe" i avventende ende
Alle gode testrammer lar deg kjøre kode før og etter hver test kjøres. RSpec er ikke annerledes.
Det gir oss før
og etter
metoder som gjør det mulig for oss å sette opp en bestemt tilstand for at testen skal løpe, og deretter rydde opp staten etter at testen har gått (dette er slik at staten ikke lekker og påvirker utfallet av påfølgende tester).
beskrive "noen metode" gjør før (: hver) gjør # noen oppsettkode slutter etter (: hver) gjør # noen nedrivningskode slutter det "skal gjøre noe" i avventende sluttend
Vi har allerede sett beskrive
blokkere; men det er en annen blokk som er funksjonelt ekvivalent kalt kontekst
. Du kan bruke den der du vil bruke beskrive
.
Forskjellen mellom dem er subtil men viktig: kontekst
tillater oss å definere en tilstand for vår test. Ikke eksplisitt skjønt (vi setter faktisk ikke staten ved å definere a kontekst
blokkere - det er i stedet for lesbarhetsformål slik at hensikten med følgende kode er klarere).
Her er et eksempel:
beskrive "noen metode" gjør konteksten "blokk gitt" gjør det "gir for å blokkere" ventende sluttendets kontekst "ingen blokk gitt" gjør det "kaller en tilbakekallingsmetode" ventende sluttendens ende
Vi kan bruke stub
Metode for å lage en falsk versjon av et eksisterende objekt og å få det til å returnere en forhåndsbestemt verdi.
Dette er nyttig for å hindre at testene våre berører APIer for direktetjeneste, og veileder testene våre ved å gi forutsigbare resultater fra bestemte samtaler.
Tenk deg at vi har en klasse kalt Person
og at denne klassen har en snakke
metode. Vi vil teste at metoden fungerer hvordan vi forventer det. For å gjøre dette stubber vi snakke
metode ved å bruke følgende kode:
beskrive Person gjør det "snakk ()" gjør bob = stub () bob.stub (: snakk) .og_return ('hallo') Person.any_instance.stub (: initialiser) .and_return (bob) instance = Person.new forvente instance.speak) .til eq ('hallo') slutten
I dette eksemplet sier vi at "noen forekomst" av Person
klassen skal ha sin initial
Metoden stubbet slik at den returnerer objektet bob
.
Du vil legge merke til det bob
er selv en stub som er satt opp slik at enhver tidskode forsøker å utføre snakke
metoden det vil returnere "hei".
Vi fortsetter deretter med å opprette en ny Person
forekomst og send anropet til instance.speak
inn i RSpec s forvente
syntaks.
Vi forteller RSpec at vi forventer at samtalen for å resultere i strengen "hallo".
I de tidligere eksemplene har vi RSpec-funksjonen and_return
for å indikere hva vår stub skal returnere når den kalles.
Vi kan angi en annen returverdi hver gang stubben kalles ved å angi flere argumenter til and_return
metode:
obj = stub () obj.stub (: foo) .og_return (1, 2, 3) forvente (obj.foo ()). til eq (1) forventer (obj.foo ()). (obj.foo ()). til eq (3)
Mocks ligner Stubs fordi vi skaper falske versjoner av våre objekter, men i stedet for å returnere en forhåndsdefinert verdi, styrer vi nærmere bestemt ruterne våre objekter må ta for testen å være gyldig.
For å gjøre det bruker vi håne
metode:
Beskriv Obj gjør det "testing" ("bob") testing ('noen verdi') slutten
I eksempelet ovenfor lager vi en ny Gjenstand
eksempel og deretter ringe det testing
metode.
Bak kulissene i den koden forventer vi testing
metode som skal kalles med verdien 'innhold'
. Hvis det ikke kalles med den verdien (som i eksempelet ovenfor er det ikke) vet vi at noe av koden vår ikke har fungert riktig.
De Emne
Søkeordet kan brukes på flere forskjellige måter. Alle er designet for å redusere kod duplisering.
Du kan bruke det implisitt (legge merke til vår den
blokk refererer ikke til Emne
i det hele tatt):
beskriv Array beskriver "med 3 elementer" gjør emne [1,2,3] det should_not be_empty slutten
Du kan bruke det eksplisitt (legg merke til vår den
blokk refererer til Emne
direkte):
beskriv MyClass gjør beskrivelsen "initialisering" gjør emnet MyClass det "skaper en ny forekomst" do instance = subject.new forvente (forekomst) .til be_a (MyClass) endeendens ende
Snarere enn å konstant referere til et emne innenfor koden din og passere i forskjellige verdier for instantiering, for eksempel:
beskriv "Foo" gjør kontekst "A" gjør det "Bar" gjør baz = Baz.new ('a') forvente (baz.type) .til eq ('a') ende ende kontekst "B" gjør det "Bar" gjør baz = Baz.new ('b') forventer (baz.type) .til eq ('b') slutten kontekst "C" gjør det "Bar" gjør baz = Baz.new ('c') forventer .type) .til eq ('c') endeendens ende
Du kan i stedet bruke Emne
sammen med la
for å redusere duplisering:
Beskriv person "Person" gjør emne Person.new (navn) # Person har en get_name metode kontekst "Bob" gjør la (: navn) 'Bob' sin (: get_name) should == 'Bob' slutt kontekst "Joe" gjør det (name 'Joe' sin (: get_name) should == 'Joe' slutt kontekst "Smith" == 'Smith' slutten
Det er mange flere funksjoner tilgjengelig for RSpec, men vi har sett på de viktigste som du finner deg selv å bruke mye når du skriver tester ved hjelp av RSpec.
Du kan konfigurere RSpec til å kjøre testene dine i en tilfeldig rekkefølge. Dette gjør det mulig å sikre at ingen av testene dine har tillit eller avhengighet av de andre testene rundt den.
RSpec.configure do | config | config.order = 'random' slutt
Du kan angi dette via kommandoen ved hjelp av --rekkefølge
flagg / option. For eksempel: rspec - rekkefølge tilfeldig
.
Når du bruker --rekkefølge tilfeldig
alternativet RSpec vil vise tilfeldig tallet som brukes til å frøke algoritmen. Du kan bruke denne "frøverdien" igjen når du tror du har oppdaget et avhengighetsproblem i testene dine. Når du har løst det du mener er problemet, kan du sende frøverdien til RSpec (for eksempel hvis frøet var 1234
Kjør deretter --rekkefølge tilfeldig: 1234
) og det vil bruke det samme randomiserte frøet for å se om det kan replikere den opprinnelige avhengighetsbuggen.
Du har sett at vi har lagt til et prosjektspesifikt sett med konfigurasjonsobjekter i vår Rakefile
. Men du kan angi konfigurasjonsalternativer globalt ved å legge dem til en .rspec
filen i din hjemmekatalog.
For eksempel, inne .rspec
:
--farge - format nestet
Nå er vi klare til å begynne å se hvordan vi kan feilsøke vår søknad og vår testkode ved hjelp av Pry-perlen.
Det er viktig å forstå at selv om Pry er veldig bra for feilsøking av koden, er den faktisk ment som et forbedret Ruby REPL-verktøy (for å erstatte IRB
) og ikke strengt feilsøking formål; så for eksempel er det ingen innebygde funksjoner som: gå inn i, gå over eller gå ut osv. som du vanligvis vil finne i et verktøy utviklet for feilsøking.
Men som et feilsøkingsverktøy er Pry veldig fokusert og magert.
Vi kommer tilbake til debugging i et øyeblikk, men la oss først vurdere hvordan vi bruker Pry i utgangspunktet.
For å demonstrere Pry, skal jeg legge til mer kode i eksempeleksemplet mitt (denne ekstra koden påvirker ikke vår test på noen måte)
klasse RSpecGreeter attr_accessor: test @@ class_property = "Jeg er en klasse eiendom" def greet binding.pry @instance_property = "Jeg er en forekomst eiendom" pubs privs "Hei RSpec!" end def pubs test_var = "Jeg er en testvariabel" test_var end private def privs setter "Jeg er privat" slutten
Du vil legge merke til at vi har lagt til noen ekstra metoder, eksempel- og klasseegenskaper. Vi ringer også til to av de nye metodene vi har lagt til fra vår side hilse på
metode.
Til slutt vil du legge merke til bruken av binding.pry
.
binding.pry
Et bruddpunkt er et sted i koden din hvor henrettelsen vil stoppe.
Du kan ha flere bruddpunkter angitt i koden din, og du lager dem ved å bruke binding.pry
.
Når du kjører koden din, vil du legge merke til at terminalen vil stoppe og plassere deg inne i søknadens kode på det nøyaktige stedet din binding.pry ble plassert.
Nedenfor er et eksempel på hvordan det kan se ut ...
8: def greet => 9: binding.pry 10: pubber 11: privs 12: "Hei RSpec!" 13: slutten
Fra dette punktet har Pry tilgang til det lokale omfanget, slik at du kan bruke Pry mye som du ville i IRB
og begynn å skrive inn for eksempel variabler for å se hvilke verdier de holder.
Du kan kjøre exit
kommandoen for å avslutte Pry og for koden din for å fortsette å utføre.
Hvor er jeg
Når du bruker mye av binding.pry
bruddpunkter kan det være vanskelig å forstå hvor i søknaden du er.
For å få en bedre sammenheng med hvor du er når som helst, kan du bruke Hvor er jeg
kommando.
Når du kjører på egenhånd, ser du noe som ligner på når du brukte binding.pry
(du ser linjen som brytepunktet ble satt inn og et par linjer over og under det). Forskjellen er at hvis du passerer et ekstra numerisk argument som whereami 5
Du ser ytterligere fem linjer over hvor binding.pry
var plassert. Du kan be om å se 100 linjer rundt det aktuelle brytepunktet for eksempel.
Denne kommandoen kan hjelpe deg med å orientere deg i den nåværende filen.
wtf
De wtf
kommando står for "hva f ***" og det gir en full stakk spor for det siste unntaket som er kastet. Det kan hjelpe deg å forstå trinnene som fører opp til feilen som oppstod.
ls
De ls
Kommandoen viser hvilke metoder og egenskaper som er tilgjengelige for Pry.
Når det kjører, vil det vise deg noe som ...
RSpecGreeter # metoder: hilse pubs test test = klassevariabler: @@ class_property lokalbefolkningen: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
I eksempelet ovenfor kan vi se at vi har fire offentlige metoder (husk vi oppdaterte koden vår for å inkludere noen ekstra metoder og deretter test
og test =
ble opprettet ved bruk av Ruby's attr_accessor
kort hånd).
Det viser også andre klasse- og lokale variabler som Pry kan få tilgang til.
En annen nyttig ting du kan gjøre er å grep (søk) resultatene for bare det du er interessert i. Du må forstå for Regular Expressions, men det kan være en praktisk teknikk. Her er et eksempel ...
ls -p -G ^ p => RSpecGreeter # metoder: privs
I eksemplet ovenfor bruker vi -p
og -G
alternativer / flagg som forteller Pry vi vil bare se offentlige og private metoder og vi bruker regex ^ p
(som betyr samsvarer med alt som begynner med p
) som vårt søkemønster for å filtrere resultatene.
Løping ls - hjelp
vil også vise deg alle tilgjengelige alternativer.
cd
Du kan endre det nåværende omfanget ved å bruke cd
kommando.
I vårt eksempel hvis vi kjører cd ... / puber
det vil ta oss til resultatet av denne metoden samtale.
Hvis vi løper nå Hvor er jeg
du vil se det vil vise Inne i "Jeg er en testvariabel"
.
Hvis vi løper selv-
så ser vi vi får "Jeg er en testvariabel"
returnert.
Hvis vi løper self.class
vi får se string
returnert.
Du kan bevege deg opp i omfangskæden ved hjelp av cd ...
eller du kan gå tilbake til det øverste nivået av omfanget ved å bruke cd /
.
Merk: Vi kunne legge til en annen binding.pry
inne i puber
metode og da vil vårt omfang være innenfor denne metoden i stedet for resultatet av metoden.
hekkende
Tenk på forrige eksempel på kjøring cd-puber
. Hvis vi kjører hekkende
kommando vi får et toppnivå utseende på antall sammenhenger / nivåer Pry har for øyeblikket:
Nestestatus: - 0. # (Pry toppnivå) 1. "Jeg er en testvariabel"
Derfra kan vi løpe exit
å flytte tilbake til den tidligere konteksten (for eksempel, inne i hilse på
metode)
Løping exit
igjen betyr at vi lukker den siste konteksten Pry har og så Pry er ferdig, og vår kode fortsetter å løpe.
finn-metoden
Hvis du ikke er sikker på hvor du skal finne en bestemt metode, kan du bruke finn-metoden
kommandoen for å vise deg alle filene i koden din, som har en metode som samsvarer med det du søker etter:
finn-metode priv => Kernekjerne # private_methods Modulmodul # private_instance_methods Modul # private_constant Modul # private_method_defined? Modul # private_class_method Modul # privat RSpecGreeter RSpecGreeter # privs
Du kan også bruke -c
alternativ / flagg for å søke i innholdet av filer i stedet:
finne-metode -c greet => RSpecGreeter RSpecGreeter: def greet RSpecGreeter # privs: greet
neste
, skritt
, Fortsette
Selv om de ovennevnte teknikkene er nyttige, er det egentlig ikke "feilsøking" i samme forstand som det du sannsynligvis pleide å.
For de fleste utviklere vil deres redaktør eller nettleser gi dem et innebygd feilsøkingsverktøy som lar dem faktisk gå gjennom kodelinjen for linje og følge ruten koden tar til ferdigstillelse.
Som Pry er utviklet for å bli brukt som en REPL som ikke er å si det ikke er nyttig for feilsøking.
En naiv løsning ville være å sette flere binding.pry
uttalelser gjennom en metode og bruk ctrl-dg å bevege seg gjennom hvert bruddpunktssett. Men det er fortsatt ikke helt bra nok.
For trinnvis feilsøking kan du laste inn perle pry-nav ...
kilde "https://rubygems.org" gem 'rspec' gruppe: utvikling gjør gem 'guard' perle 'guard-rspec' perle 'pry' # Legger til feilsøkingstrinn til Pry # fortsett, trinn, neste perle 'pry-remote' perle 'pry-nav' ende
Denne perlen strekker seg Pry så det forstår følgende kommandoer:
neste
(flytt til neste linje)Skritt
(flytt til neste linje, og hvis det er en metode, så flytt inn i den metoden)Fortsette
(Ignorer eventuelle flere bruddpunkter i denne filen)Som en ekstra bonus, la oss integrere våre tester med den elektroniske CI (kontinuerlig integrering) tjenesten Travis-CI.
Prinsippet om CI er å forplikte / presse tidlig og ofte for å unngå konflikter mellom koden din og hovedgrenen. Når du gjør det (i dette tilfellet vi forplikter oss til GitHub), bør det slå av en "build" på CI-serveren din som kjører relevante tester for å sikre at alt fungerer som det skal være.
Nå med TDD som din primære utviklingsmetode er du mindre sannsynlig å pådra seg feil hver gang du presser fordi testene dine er en integrert del av utviklingsarbeidet ditt, og så før du presser, vil du allerede være oppmerksom på feil eller regresjoner. Men dette beskytter ikke nødvendigvis deg mot feil som oppstår fra integrasjonstester (der all kode over flere systemer kjøres sammen for å sikre at systemet som helhet fungerer som det skal).
Uansett, koden må aldri presses direkte til din liveproduksjonsserver på noen måte; det bør alltid skyves først til en CI-server for å få tak i eventuelle potensielle feil som oppstår som følge av forskjeller mellom utviklingsmiljø og produksjonsmiljø.
Mange bedrifter har fortsatt flere miljøer for at koden skal passere før den når liveproduksjonsserveren.
For eksempel på BBC News har vi:
Selv om hvert miljø skal være identisk i oppsettet, er formålet å implementere ulike typer test for å sikre at mange bugs blir fanget og løst før koden når 'live'.
Travis CI er en vert for kontinuerlig integreringstjeneste for open source-fellesskapet. Den er integrert med GitHub og tilbyr førsteklasses støtte for flere språk
Hva dette betyr er at Travis-CI tilbyr gratis CI-tjenester for open source-prosjekter, og har også en betalt modell for bedrifter og organisasjoner som ønsker å beholde CI-integrasjonen privat.
Vi bruker den gratis open source-modellen på vårt eksempel GitHub-depot.
Prosessen er dette:
.travis.yml
fil i rotkatalogen av prosjektet ditt og legg det til GitHub-depotet dittDet siste trinnet er det viktigste (å skape en .travis.yml
fil) da dette bestemmer konfigurasjonsinnstillingene for Travis-CI, slik at den vet hvordan man skal håndtere testene for prosjektet.
La oss ta en titt på .travis.yml
fil vi bruker til vårt eksempel GitHub-depot:
språk: ruby cache: bundler rvm: - 2.0.0 - 1.9.3 script: 'bundle exec rake spec' bundler_args: - uten utviklingsgrener: bare: - hovedvarsler: e-post: - [email protected]
La oss bryte ned dette stykket etter hvert ...
Først angir vi hvilket språk vi bruker i vårt prosjekt. I dette tilfellet bruker vi Ruby: språk: rubin
.
Fordi kjører Bundler kan være litt treg og vi vet at våre avhengigheter ikke kommer til å forandre det ofte, kan vi velge å cache avhengighetene, så vi setter cache: bundler
.
Travis-CI bruker RVM (Ruby Version Manager) for å installere Rubies på sine servere. Så vi må spesifisere hvilke Ruby-versjoner vi ønsker å kjøre våre tester mot. I dette tilfellet har vi valgt 2.0
og 1.9.3
som er to populære Ruby-versjoner (teknisk bruker vår søknad Ruby 2, men det er godt å kjenne våre kodepass i andre versjoner av Ruby også):
rvm: - 2.0.0 - 1.9.3
For å kjøre testene våre vet vi at vi kan bruke kommandoen rake
eller rake spec
. Travis-CI kjører som standard kommandoen rake
men på grunn av hvordan Gems er installert på Travis-CI ved hjelp av Bundler, må vi endre standardkommandoen: script: 'bundle exec rake spec'
. Hvis vi ikke gjorde dette, ville Travis-CI ha et problem å lokalisere rspec / kjerne / rake_task
fil som er spesifisert i vår Rakefile
.
Merk: Hvis du har problemer med Travis-CI, kan du bli med #travis-kanalen på IRC freenode for å få hjelp til å svare på eventuelle spørsmål du måtte ha. Det var der jeg oppdaget løsningen på problemet med Travis-CI, og jeg kunne ikke kjøre testene mine ved hjelp av standard rake
kommando og forslag om å overskrive standard med bundle exec rake
Løst det problemet.
Siden vi bare er interessert i å kjøre testene våre, kan vi passere flere argumenter til Travis-CI for å filtrere perler vi ikke vil plage å installere. Så for oss vil vi utelukke å installere edelstener gruppert som utvikling: bundler_args: - uten utvikling
(dette betyr at vi unngår edelstener som bare er virkelig brukt til utvikling og feilsøking som Pry og Guard).
Det er viktig å merke seg at jeg opprinnelig var laster inn Pry innenfor vårt spec_helper.rb
fil. Dette forårsaket et problem når du kjørte koden på Travis-CI, nå da jeg utelukket "utviklings" edelstener. Så måtte jeg justere koden slik:
krever "pry" hvis ENV ['APP_ENV'] == 'debug'
Du kan se at nå er Pry-perlen bare krever
'ed hvis en miljøvariabel for APP_ENV
er satt til feilsøking. På denne måten kan vi unngå Travis-CI å kaste eventuelle feil. Dette betyr at når du kjører koden lokalt, må du sette miljøvariabelen dersom du vil feilsøke koden din ved hjelp av Pry. Følgende viser hvordan dette kan gjøres i en linje:
APP_ENV = feilsøking && ruby lib / example.rb
Det var to andre endringer jeg laget, og det var til vår Gemfile
. En var å gjøre det klart hvilke perler som var nødvendige for testing og som var nødvendig for utvikling, og den andre var eksplisitt krevd av Travis-CI:
kilde "https://rubygems.org" gruppe: test gjør gem 'rake' gem 'rspec' sluttgruppe: utvikling gjør gem 'guard' perle 'guard-rspec' perle 'pry' # Legger til debugging skritt for å Pry # fortsette, trinn, neste perle 'pry-remote' perle 'pry-nav' slutten
Ser på ovenstående oppdatert Gemfile
vi kan se at vi har flyttet RSpec-perlen til en ny test
gruppe, så nå bør det være klarere hvilken hensikt hver perle har. Vi har også lagt til en ny perle "rake"
. Travis-CI-dokumentasjonen sier at dette må spesifiseres eksplisitt.
Det neste avsnittet er valgfritt, og det gjør at du kan legge til hvite liste (eller svarteliste) bestemte grener på lageret ditt. Derfor vil Travis-CI som standard kjøre tester mot alle grenene dine, med mindre du fortelle det ellers. I dette eksemplet forteller vi det vi vil bare at det skal løpe mot vår herre
gren:
grener: bare: - master
Vi kan fortelle det å drive hver gren "unntatt" en bestemt gren, slik som:
grener: unntatt: - some_branch_I_dont_want_run
Den siste delen forteller Travis-CI hvor du skal sende meldinger til når en bygning mislykkes eller lykkes:
varsler: e-post: - [email protected]
Du kan angi flere e-postadresser hvis du ønsker det:
varsler: e-post: - [email protected] - [email protected] - [email protected]
Du kan være mer spesifikk og angi hva du vil skje på enten en feil eller en suksess (for eksempel vil flere personer bare være interessert hvis testene mislykkes i stedet for å få en e-post hver gang de passerer):
varsler: e-post: mottakere: - [email protected] on_failure: endre on_success: aldri
Eksempelet ovenfor viser at mottakeren aldri vil motta en e-post hvis testene passerer, men vil bli varslet om feilstatusen endres (standardverdien for begge er alltid
noe som betyr at du alltid vil bli varslet uansett hva statusresultatet er).
Merk: Når du spesifikt angir en on_failure
eller on_success
du må flytte e-postadressen (e) inne i mottakere
nøkkel.
Dette er slutten av vår todel, se på RSpec, TDD og Pry.
I del ett klarte vi å skrive søknaden vår ved hjelp av TDD-prosessen og RSpec-testrammen. I denne andre halvdelen har vi også brukt Pry for å vise hvordan vi lettere kan feilsøke en løpende Ruby-applikasjon. Endelig var vi i stand til å få vår testoppsett å kjøre som en del av en kontinuerlig integrasjonsserver ved hjelp av den populære Travis-CI-tjenesten.
Forhåpentligvis har dette gitt deg nok av en smak, så du er ivrig etter å undersøke hver av disse teknikkene ytterligere.