2012-02-09 14 views
15

ScenarioAffermare che una particolare eccezione viene generata in Cetriolo

Sto scrivendo una libreria (nessun Ruby on Rails) per il quale mi piacerebbe avere caratteristiche molto dettagliate cetriolo. Ciò include in particolare la descrizione di errori/eccezioni che dovrebbero essere lanciati in vari casi.

Esempio

Il modo più intuitivo per scrivere i passi di cetriolo sarebbe probabilmente qualcosa come

When I do something unwanted 
Then an "ArgumentError" should be thrown 

Problema

Ci sono due problemi che devo affrontare:

  1. Il primo passaggio non dovrebbe fallire quando viene lanciata un'eccezione.
  2. L'eccezione che i primi passi devono essere accessibili al secondo passaggio per fare un po 'di magia dell'asserzione.

Unelegant e ingombrante soluzione

L'approccio migliore che ho potuto venire in mente è la memorizzazione nella cache l'eccezione nella prima fase e di metterlo in una variabile di istanza che la seconda fase può accedere, in questo modo:

When /^I do something unwanted$/ do 
    begin 
    throw_an_exception! 
    rescue => @error 
    end 
end 

Then /^an "(.*)" should be thrown$/ do |error| 
    @error.class.to_s.should == error 
end 

Tuttavia, ciò rende il primo passo più o meno inutili nei casi in cui ho non vuole il fallimento, e richiede una variabile di istanza, che non è mai una buona cosa.

Quindi, qualcuno può aiutarmi con una soluzione almeno meno ingombrante? O dovrei scrivere le mie caratteristiche in modo diverso comunque? Qualsiasi aiuto sarebbe molto apprezzato.

+0

Ora che la domanda è ben scritta! Merita sicuramente una buona risposta (scusa se non sono riuscito a inventarne uno) :) – user562529

+0

Grazie mille @ user562529. Sfortunatamente ho la sensazione che in realtà non ci sia una soluzione facile a questo problema, quindi potrei semplicemente aprire un problema con Cucumber e sperare che implementino qualcosa di pratico. – JLimperg

risposta

5

ho pensato ancora una volta, e forse la risposta è:

Non v'è alcuna soluzione elegante, perché il Given-When-Then -Scheme è violata nel tuo caso. Ci si aspetta che "Quindi si debba lanciare un'eccezione" è il risultato di "Quando faccio qualcosa di indesiderato".

Ma quando ci pensate, questo non è vero! L'eccezione non è il risultato di questa azione, infatti l'eccezione mostra solo che la dichiarazione "Quando" non è riuscita.

La mia soluzione a questo sarebbe di testare ad un livello superiore:

When I do something unwanted 
Then an error should be logged 

o

When I do something unwanted 
Then the user should get an error message 

o

When I do something unwanted 
Then the program should be locked in state "error" 

o una combinazione di questi.

Quindi si "memorizza l'eccezione" nel programma, il che ha perfettamente senso, poiché è molto probabile che sia necessario farlo comunque.

I due problemi che hai dichiarato sarebbero stati risolti.

Nel caso in cui si ha realmente necessario verificare eccezioni

Beh, immagino quindi cetriolo non è la suite di test giusto, eh? ;-)

Come il Dato-Quando-Then-Scheme viene violata in ogni caso, vorrei semplicemente scrivere

When I do something unwanted it should fail with "ArgumentError" 

e nelle definizioni step qualcosa di simile (non testato, per favore correggetemi se lo provate)

When /^I do something unwanted it should fail with "(.*)"$/ do |errorstring| 
    expect { 
    throw_an_exception! 
    }.to raise_error(errorstring) 
end 

Come detto sopra, è orribilmente sbagliato quando lo schema è rotto, ma servirebbe allo scopo, non è vero? ;-)

Troverete ulteriore documentazione a errori di test at rspec expectations.

+0

Oh bene ... Ho appena letto di nuovo le tue prime frasi. Non importa se la risposta è più o meno inutile come la tua scrittura di una biblioteca. – user562529

+1

Grazie per questa ampia recensione. Sfortunatamente, anche il tuo commento ha colpito il chiodo sulla testa. ;) Tuttavia, ciò che si dice è probabilmente ancora corretto e rilevante per questo problema: Cucumber potrebbe non essere progettato per scavare in profondità nella gestione degli errori di una libreria. Sembra che resterò con RSpec per questo problema. – JLimperg

+0

Ho incontrato questo stesso problema e sono giunto alla conclusione che mentre Cucumber è una grande libreria per testare le applicazioni, non è adatto per testare le librerie. Il punto di forza di Cucumber è che può essere letto da persone che non leggono il codice e pochissimi non sviluppatori potrebbero essere interessati a leggere le specifiche per una libreria di sviluppatori, quindi ho optato per Cucumber per le app e RSpec per le librerie . Questa è una buona risposta per le persone come me che trovano questa pagina mentre sviluppano un'applicazione. In tal caso, scrivere le specifiche ad un livello più alto è più appropriato. –

4

Un'opzione è contrassegnare lo scenario con @allow-rescue e controllare l'output e il codice di stato della pagina. Per esempio

In my_steps.rb

Then(/^the page (?:should have|has) content (.+)$/) do |content| 
    expect(page).to have_content(content) 
end 

Then(/^the page should have status code (\d+)$/) do |status_code| 
    expect(page.status_code.to_s).to eq(status_code) 
end 

Then /^I should see an error$/ do 
    expect(400..599).to include(page.status_code) 
end 

In my_feature.feature

@allow-rescue 
Scenario: Make sure user can't do XYZ 
    Given some prerequisite 
    When I do something unwanted 
    Then the page should have content Routing Error 
    And the page should have status code 404 

o in alternativa:

@allow-rescue 
Scenario: Make sure user can't do XYZ 
    Given some prerequisite 
    When I do something unwanted 
    Then I should see an error 

Questo potrebbe non essere esattamente quello che speravano, ma potrebbe essere un soluzione accettabile per alcune persone che si imbattono in questa pagina. Penso che dipenderà dal tipo di eccezione, dal momento che se l'eccezione non viene salvata a nessun livello, lo scenario fallirà comunque. Ho usato questo approccio principalmente per errori di routing finora, che ha funzionato bene.

-1

sto rispondendo dal punto di vista di qualcuno che utilizza le funzionalità di cetriolo in una situazione behavior-driven development, in modo da prendere o lasciare ...

Gli scenari dovrebbero essere scritti per testare una 'funzione' o la funzionalità dell'applicazione, anziché essere utilizzato per testare il codice stesso. Un esempio è:

When the service is invoked 
Then a success code should be returned 

Sembra che il tuo banco di prova (cioè se faccio questo, allora questa eccezione dovrebbe essere gettato) è un candidato per unità o test di integrazione - nel mio caso, useremmo qualche deriso o quadro di test unitario.

Il mio suggerimento sarebbe quello di rivalutare i propri scenari di funzionalità per vedere se stanno veramente testando ciò che si intende testare. Dall'esperienza personale, ho scoperto che se le mie classi di test stanno diventando anormalmente complesse, allora le mie funzionalità sono "sbagliate".

1

È possibile generare un'eccezione in un blocco When e quindi fare asserzioni su di esso nei seguenti blocchi Then.

Usando il tuo esempio:

When /^I do something unwanted$/ do 
    @result = -> { throw_an_exception! } 
end 

Then /^an "(.*)" should be thrown$/ do |error| 
    expect{ @result.call }.to raise_error(error) 
end 

Questo esempio utilizza matchers s' RSpec ma la parte importante è la -> (Lambda); che consente il rinvio del riferimento al metodo throw_an_exception!.

Spero che questo aiuti!

Problemi correlati