2013-07-17 8 views
17

Voglio eseguire è ruby sayhello.rb sulla riga di comando, quindi ricevere Hello from Rspec.Come verificare mette in rspec

c'ho che con questo:

class Hello 
    def speak 
    puts 'Hello from RSpec' 
    end 
end 

hi = Hello.new #brings my object into existence 
hi.speak 

Ora voglio scrivere un test in rspec per verificare che l'output della riga di comando è di fatto "Ciao da RSpec" e non "mi piace Unix "

NON FUNZIONANTE. Al momento ho questo nel mio file di sayhello_spec.rb

require_relative 'sayhello.rb' #points to file so I can 'see' it 

describe "sayhello.rb" do 
    it "should say 'Hello from Rspec' when ran" do   
    STDOUT.should_receive(:puts).with('Hello from RSpec')  
    end 
end 

Inoltre, ho bisogno di vedere in realtà ciò che il test dovrebbe essere simile nella mia RSPEC favore.

risposta

13

Si sta eseguendo il codice prima di entrare nel blocco di test, quindi le aspettative non vengono soddisfatte. È necessario eseguire il codice all'interno del blocco di prova dopo aver impostato le aspettative (ad esempio spostando la dichiarazione require_relative dopo l'istruzione STDOUT....), come segue:

describe "sayhello.rb" do 
    it "should say 'Hello from Rspec' when ran" do   
    STDOUT.should_receive(:puts).with('Hello from RSpec') 
    require_relative 'sayhello.rb' #load/run the file 
    end 
end 
+0

Come rspec dovrebbe conoscere il codice in 'sayhello.rb'? – bswinnerton

+1

RSpec non ha bisogno di conoscere (cioè accedere) il codice in 'sayhello.rb'. La specifica sta semplicemente ponendo un'aspettativa su 'STDOUT.puts', che è incorporato in Ruby. L'aspettativa è soddisfatta dopo che 'sayhello.rb' è caricato ed eseguito (ad esempio' require'd). –

+5

Questa è la stessa aspettativa con la nuova sintassi di RSpec: 'expect (STDOUT) .per ricevere (: puts) .with (" Hello da RSpec ")' – Jankeesvw

11

È possibile risolvere questo usando active_support biblioteca Rails', che aggiunge un capture metodo:

require 'active_support/core_ext/kernel/reporting' 
require_relative 'sayhello' 

describe Hello do 
    it "says 'Hello from RSpec' when ran" do 
    output = capture(:stdout) do 
     hi = Hello.new 
     hi.speak 
    end 
    expect(output).to include 'Hello from RSpec' 
    end 
end 
+10

'# capture' è deprecato e verrà rimosso in Rails 5. https://github.com/rails/rails/blob/dd7bd8c023696657a600ee5dba16bfe5def876bf/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L91 – barelyknown

9

in base alle risposte precedenti/commenti, una soluzione che utilizza la nuova sintassi senza un gioiello sarebbe simile a questa:

describe "sayhello.rb" do 
    it "should say 'Hello from Rspec' when run" do   
    expect(STDOUT).to receive(:puts).with('Hello from RSpec') 
    require_relative 'sayhello.rb' # load/run the file 
    end 
end 
3

Un po 'simile alla risposta di bswinnerton, uno può acquisire l'output puts e quindi testare l'output acquisito, senza dover utilizzare il metodo capture dipendente dalla libreria (che qualcuno ha menzionato è deprecato in Rails 5).

Ruby ha una variabile globale denominata $stdout che per impostazione predefinita è popolata dalla costante STDOUT. STDOUT è quello che invia i dati al flusso stdout del processo ruby ​​(non è sicuro se "stream" sia il termine giusto qui). Fondamentalmente in un caso ingenuo, STDOUT.puts("foo"), apparirà "foo \ n" nella finestra del terminale. $stdout.puts("foo") farà la stessa cosa perché il nome della variabile $stdout si riferisce a STDOUTa meno che non lo riassegni (punto chiave qui). Infine puts("foo") è zucchero sintattico per $stdout.puts("foo").

La strategia è quindi di riassegnare $stdout ad un IO un'istanza locale che è possibile controllare dopo l'esecuzione del codice, per vedere se "Ciao da RSpec" si presentò nei suoi contenuti.

Come questo dovrebbe funzionare:

describe "sayhello.rb" do 
    it "should say 'Hello from Rspec' when ran" do   
    $stdout = StringIO.new 

    # run the code 
    # (a little funky; would prefer Hello.new.speak here but only changing one thing at a time) 
    require_relative 'sayhello.rb' 

    $stdout.rewind # IOs act like a tape so we gotta rewind before we play it back 

    expect($stdout.gets.strip).to eq('Hello from Rspec') 
    end 
end 
+0

Questo metodo può essere utilizzato per verificare la presenza di più messaggi di stdout? (a confronto le altre risposte che ne controllano una) –

+2

@matrim_c: so che è tardi per te ma rispondi a questo per chiunque altro venga qui. Sì, puoi usare questo per testare più messaggi stdout. L'oggetto StringIO funziona come un file, con più righe. 'gets' è un comando per leggere una singola riga (incluso il carattere di nuova riga' \ n'), quindi spostare la testina di lettura sul primo carattere della riga successiva. Chiamando 'gets' più volte si otterranno le righe in ordine in cui sono state aggiunte con' puts'. Chiamando 'read' su StringIO si otterrà l'intero contenuto, compresi i caratteri' \ n' tra le righe. –

7

Penso che il modo migliore è quello di utilizzare rspec costruire matcher uscita https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher

Fore esempio, questa è la tua classe

class MakeIt 
    def awesome(text) 
    puts "Awesome #{text}" 
    end 
end 

e il tuo test

describe MakeIt do 
    describe '#awesome' do 
    it 'prints awesome things' do 
     expect do 
     MakeIt.new.awesome('tests') 
     end.to output('Awesome tests').to_stdout 
    end 

    it 'does not print not awesome things' do 
     expect do 
     MakeIt.new.awesome('tests') 
     end.to_not output('Not awesome tests').to_stdout 
    end 
    end 
end 

Bello, pulito e dal libro!