Ruby for Newbies test con Rspec

Ruby è una delle lingue più popolari utilizzate sul web. Stiamo eseguendo una sessione qui su Nettuts + che ti introdurrà a Ruby, così come i grandi framework e strumenti che accompagnano lo sviluppo di Ruby. In questo episodio, imparerai a testare il tuo codice Ruby con Rspec, una delle migliori librerie di test del settore.


Preferisci uno Screencast?

Sembra familiare?

Se hai letto il mio recente tutorial su JasmineJS, probabilmente noterai diverse somiglianze in Rspec. In realtà, le somiglianze sono in Jasmine: Jasmine è stato creato pensando a Rspec. Vedremo come usare Rspec per fare TDD in Ruby. In questo tutorial, creeremo alcune classi di Ruby forzate per familiarizzare con la sintassi di Rspec. Tuttavia, il prossimo? Ruby for Newbies? episodio sarà caratterizzato dall'uso di Rspec in congiunzione con alcune altre librerie per testare le app web? quindi rimanete sintonizzati!


Impostare

È piuttosto semplice installare Rspec. Apri la riga di comando e esegui questo:

gem install rspec

Così facile.

Ora, impostiamo un piccolo progetto. Creeremo due classi: Libro e Biblioteca. Nostro Libro gli oggetti memorizzeranno solo un titolo, autore e categoria. Nostro Biblioteca l'oggetto memorizzerà un elenco di libri, li salverà in un file e ci consentirà di recuperarli per categoria.

Ecco come dovrebbe essere la directory del tuo progetto:

Inseriamo le specifiche (o le specifiche) in a spec cartella; abbiamo un file spec per ogni classe. Notare il spec_helper.rb file. Per far funzionare le nostre specifiche, è necessario richiedere le classi Ruby che stiamo testando. Questo è quello che stiamo facendo all'interno del spec_helper file:

require_relative '? / library 'require_relative'? / libro 'richiede' yaml '

(Hai incontrato require_relative ancora? No? Bene, require_relative è proprio come richiedere, tranne che invece di cercare il tuo percorso Ruby, cerca parente alla directory corrente.)

Potresti non avere familiarità con il modulo YAML; YAML è un semplice database di testo che useremo per memorizzare i dati. Vedrai come funziona e ne parleremo in seguito.

Quindi, ora che siamo pronti, creiamo delle specifiche!


Il Libro Classe

Iniziamo con i test per il Libro classe.

richiedere 'spec_helper' descrivere Book do end

Ecco come iniziamo: con a descrivere bloccare. Il nostro parametro a descrivere spiega cosa stiamo testando: potrebbe trattarsi di una stringa, ma nel nostro caso stiamo usando il nome della classe.

Quindi cosa abbiamo intenzione di mettere dentro questo descrivere bloccare?

prima: ogni do @book = Book.new "Title", "Author",: category end

Inizieremo chiamando prima; noi passiamo il simbolo :ogni per specificare che vogliamo eseguire questo codice prima di ogni test (potremmo anche farlo :tutti per eseguirlo una volta prima di tutti i test). Cosa stiamo facendo esattamente prima di ogni test? Stiamo creando un'istanza di Libro. Si noti come lo stiamo trasformando in una variabile di istanza, anticipando il nome della variabile con @. Dobbiamo fare in modo che la nostra variabile sia accessibile dai nostri test. Altrimenti, otterremo solo una variabile locale che è solo buona all'interno del prima bloccare? che non va affatto bene.

Andare avanti,

descrivi "#new" fallo "prende tre parametri e restituisce un oggetto Book" do @ book.should be_an_instance_of Book end end

Ecco il nostro primo test. Stiamo usando un nidificato descrivere blocca qui per dire che stiamo descrivendo le azioni di un metodo specifico. Noterai che ho usato la stringa? #New ?; è una convenzione in Ruby per parlare di metodi di istanza come questo: ClassName # nomeMetodo Dal momento che abbiamo il nome della classe nel nostro livello superiore descrivere, stiamo solo mettendo il nome del metodo qui.

Il nostro test semplicemente conferma che siamo effettivamente fatti un oggetto Book.

Nota la grammatica che usiamo qui: object.should do_qualcosa. Il novantanove per cento dei test avrà questo formato: hai un oggetto e inizi a chiamare dovrebbero o non dovrebbe sull'oggetto. Quindi, si passa a quell'oggetto la chiamata a un'altra funzione. In questo caso è così be_an_instance_of (che prende Libro come il suo singolo parametro). Complessivamente, questo rende un test perfettamente leggibile. È molto chiaro @libro dovrebbe essere un'istanza della classe Libro. Quindi, corriamolo.

Apri il tuo terminale, CD nella directory del progetto ed esegui spec. spec. Il spec è la cartella in cui RSpec troverà i test. Dovresti vedere un output che dica qualcosa su: costante non inizializzata Object :: Book ?; questo significa solo che non c'è Libro classe. Risolviamolo.

Secondo TDD, vogliamo solo scrivere abbastanza codice per risolvere questo problema. Nel book.rb file, sarebbe questo:

fine del libro di classe

Rieseguire il test (spec. spec), e scoprirai che sta passando bene. Non abbiamo un inizializzare metodo, quindi chiama Rubino # nuovo non ha alcun effetto in questo momento. Ma possiamo creare Libro oggetti (anche se vuoti). Normalmente, seguiremmo questo processo attraverso il resto del nostro sviluppo: scrivere un test (o alcuni test correlati), guardarlo fallire, farlo passare, refactoring, ripetere. Tuttavia, per questo tutorial, ti mostrerò solo i test e il codice e li discuteremo.

Quindi, più test per Libro:

descrivi "#titolo" fallo "restituisce il titolo corretto" do @ book.title.should eql "Titolo" end end descrive "#author" fallo "restituisce l'autore corretto" do @ book.author.should eql "Autore" fine fine descrive "#categoria" fallo "restituisce la categoria corretta" do @ book.category.should eql: end end di categoria

Dovrebbero esserci tutti abbastanza interessanti per te. Ma nota come stiamo comparando nel test: con EQL. Ci sono tre modi per testare l'uguaglianza con Rspec: usando l'operatore == o il metodo EQL entrambi ritornano vero se i due oggetti hanno lo stesso contenuto Ad esempio, entrambi sono stringhe o simboli che dicono la stessa cosa. Poi c'è pari, che restituisce solo true nei due oggetti sono veramente e veramente uguali, nel senso che sono lo stesso oggetto in memoria. Nel nostro caso, EQL (o ==) è ciò che vogliamo.

Questi falliranno, quindi ecco il codice per Libro per farli passare:

class Book attr_accessor: title,: author,: category def initialize title, author, category @title = title @author = author @category = category end end

Passiamo a Biblioteca!


Specificare il Biblioteca classe

Questo sarà un po 'più complicato. Iniziamo con questo:

richiede "spec_helper" per descrivere "Oggetto libreria" prima: tutti fanno lib_obj = [Book.new ("JavaScript: Le parti buone", "Douglas Crockford",: sviluppo), Book.new ("Progettazione con gli standard Web", " Jeffrey Zeldman ",: design), Book.new (" Non farmi pensare "," Steve Krug ",: usabilità), Book.new (" Modelli JavaScript "," Stoyan Stefanov ",: sviluppo), Libro. nuovo ("Responsive Web Design", "Ethan Marcotte",: design)] File.open "books.yml", "w" do | f | f.write YAML :: dump lib_obj end end before: each do @lib = Library.new "books.yml" end end

Questo è tutto impostato: ne stiamo usando due prima blocchi: uno per :ogni e uno per :tutti. Nel prima: tutto blocco, creiamo una serie di libri. Quindi apriamo il file? Books.yml? (in modalità? w? rite) e utilizzare YAML per scaricare l'array nel file.

Breve traccia del coniglio per spiegare meglio YAML: YAML è, secondo il sito, uno standard di serializzazione dei dati amichevole per tutti i linguaggi di programmazione. È come un database basato su testo, un po 'come JSON. Stiamo importando YAML nel nostro spec_helper.rb. Il YAML il modulo ha due metodi principali che utilizzerai: cumulo di rifiuti, che emette i dati serializzati come una stringa. Poi, caricare prende la stringa di dati e la riconduce agli oggetti Ruby.

Quindi, abbiamo creato questo file con alcuni dati. Prima :ogni test, creeremo a Biblioteca oggetto, passandogli il nome del file YAML. Ora vediamo i test:

descrivi "#new" fai contesto "senza parametri" fai "non ha libri" do lib = Library.new lib.should ha (0) .books fine fine contesto "con un parametro yaml" do it "ha cinque libri "do @ lib.should have (5) .books end end end" restituisce tutti i libri in una determinata categoria "do @ lib.get_books_in_category (: development) .length.should == 2 end it" accetta nuovi libri "do @ lib.add_book (Book.new ("Progettazione per il Web", "Mark Boulton",: design)) @ lib.get_book ("Progettazione per il Web"). should be_an_instance_of Book end it "salva la libreria" do books = @ lib.books.map | book | book.title @ lib.save lib2 = Library.new "books.yml" books2 = lib2.books.map | book | book.title books.should eql books2 end

Iniziamo con un interiore descrivere blocco soprattutto per il Biblioteca # nuova metodo. Stiamo introducendo un altro blocco qui: contesto Questo ci consente di specificare un contesto per test al suo interno o di specificare risultati diversi per situazioni diverse. Nel nostro esempio, abbiamo due diversi contesti: senza parametri? e? con un parametro file yaml ?; questi mostrano i due comportamenti da usare Biblioteca # nuova.

Inoltre, noti i test matchers che utilizziamo in questi due test: lib.should avere (0) .books e @ lib.should avere (5) .books. L'altro modo di scrivere questo sarebbe lib.books.length.should == 5, ma questo non è così leggibile. Tuttavia, mostra che abbiamo bisogno di avere un libri proprietà che è una matrice dei libri che abbiamo.

Quindi, abbiamo altri tre test per testare la funzionalità di ottenere libri per categoria, aggiungere un libro alla libreria e salvare la libreria. Questi falliscono tutti, quindi scriviamo ora il corso.

libreria di classi attr_accessor: books def initialize lib_file = false @lib_file = lib_file @books = @lib_file? YAML :: load (File.read (@lib_file)): [] end def get_books_in_category category @ books.select do | book | book.category == categoria end end def add_book book @ books.push book end def get_book title @ books.select do | book | book.title == title end.first end def save save lib_file = false @lib_file = lib_file || @lib_file || "library.yml" File.open @lib_file, "w" do | f | f.write YAML :: dump @books end end end

Potremmo scrivere più test e aggiungere molte altre funzionalità a questo Biblioteca classe, ma ci fermeremo qui. Ora in esecuzione spec. spec, vedrai che tutti i test passano.

Questo non ci dà molte informazioni sui test, però. Se vuoi vedere di più, usa il parametro di formato nidificato: rspec spec --format nested. Vedrai questo:


Pochi ultimi giocatori

Prima di concludere, lascia che ti mostri un paio di altri giocatori

  • obj.should be_true, obj.should be_false, obj.should be_nil, obj.should be_empty - i primi tre di questi potrebbero essere fatti da == vero, eccetera. be_empty sarà vero se obj.empty? è vero.
  • dovrebbe esistere - questo oggetto esiste ancora??
  • obj.should have_at_most (n) .items, object.should have_at_least (n) .items - piace avere, ma passerà se ci sono più o meno di n articoli, rispettivamente.
  • obj.should include (a [, b ,?]) - sono uno o più elementi in un array?
  • obj.should corrisponde (string_or_regex) - l'oggetto corrisponde alla stringa o regex?
  • obj.should raise_exception (errore) - questo metodo genera un errore quando viene chiamato?
  • obj.should respond_to (method_name) - questo oggetto ha questo metodo? Può assumere più di un nome di metodo, in stringhe o simboli.

Vuoi saperne di più?

Rspec è uno dei migliori framework per i test in Ruby, e c'è una tonnellata che puoi fare con esso. Per saperne di più, controlla il sito Web di Rspec. C'è anche il libro The Rspec, che insegna molto più di Rspec: è tutto basato su TDD e BDD in Ruby. Lo sto leggendo ora, ed è estremamente approfondito e approfondito.

Bene, questo è tutto per questa lezione! La prossima volta vedremo come utilizzare Rspec per testare le interfacce in un'app Web.