2010-11-10 11 views
12

Sto testando la convalida dei miei modelli con rspec e mi aspetto un messaggio di errore. Tuttavia, è probabile che il testo esatto del messaggio cambi, quindi voglio essere un po 'più indulgente e controllare solo un messaggio parziale.RSpec: corrisponde ad una serie di stringhe per regex

Dal momento che le Spec :: :: Matchers includono metodo funziona solo per archi e le collezioni, Attualmente sto usando questo costrutto:

@user.errors[:password].any?{|m|m.match(/is too short/)}.should be_true 

questo funziona, ma sembra un po 'ingombrante per me. C'è un modo migliore (vale a dire, più veloce o più ruby) per controllare un array per l'inclusione di una stringa tramite regex, o forse un rspec matcher che fa proprio questo?

+2

fare un abbinatore personalizzato – shingara

risposta

16

io consiglierei facendo

@user.errors[:password].to_s.should =~ /is too short/ 

Semplicemente perché vi darà un altro errore utile quando fallisce. Se si utilizza be_any quindi si ottiene un messaggio come questo ...

Failure/Error: @user.errors[:password].should be_any{ |m| m =~ /is too short/} 
    expected any? to return true, got false 

Tuttavia, se si utilizza il metodo to_s quindi si otterrà qualcosa di simile:

Failure/Error: @user.errors[:password].to_s.should =~ /is too short/ 
    expected: /is to short/ 
     got: "[]" (using =~) 
    Diff: 
    @@ -1,2 +1,2 @@ 
    -/is too short/ 
    +"[]" 

modo da poter vedere la ragione per il fallimento e non devi scavare molto per capire perché sta fallendo.

+0

Questa è la versione che ho sempre usato e funziona bene. Sono d'accordo con la difficoltà di capire cosa è andato storto dal messaggio di errore ambiguo quando si usa 'be_any' – nzifnab

8

Io non credo che fa la differenza di prestazioni, ma una soluzione più RSpec simile sarebbe

@user.errors[:password].should be_any { |m| m =~ /is too short/ } 
+0

Molto più bello, grazie. Ora se potessi trovare la documentazione solo per be_any ... – Thilo

+0

Per quanto riguarda i documenti, controlla http://rubydoc.info/gems/rspec-expectations/2.0.1/frames. 'be_any' viene generato dinamicamente dal predicato' any? ', che deriva da' Enumerable'. – zetetic

+0

Posso vedere come 'any?' Converte in 'be_any', ma non sembra un buon inglese. –

9

È possibile inserire il seguente codice nel spec/supporto/custom_matchers.rb

RSpec::Matchers.define :include_regex do |regex| 
    match do |actual| 
    actual.find { |str| str =~ regex } 
    end 
end 

Ora è possibile utilizzarlo in questo modo:

@user.errors.should include_regex(/is_too_short/) 

ed essere sicuri di avere qualcosa di simile in spec/spec_helper.rb

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 
+0

La migliore risposta. Grazie! –

0

Solo un'altra opzione

@user.errors[:password].grep /is too short/ 
2

La mia soluzione a questo è simile a @ muirbot di. Io uso un matcher personalizzato. Tuttavia, io uso il vero matcher include, ma lo aggiungo come parametro al matcher personalizzato. Caricalo da qualche parte prima dell'esecuzione della tua suite (ad esempio in spec/support/matchers.rb, a sua volta caricato da spec/spec_helper.RB):

RSpec::Matchers.define(:a_string_matching) do |expected| 
    match do |actual| 
    actual =~ expected 
    end 
end 

Allora la vostra aspettativa si può scrivere in questo modo:

expect(@user.errors[:password]).to include(a_string_matching(/is too short/)) 
+0

'a_string_matching' [fa già parte di RSpec] (https://github.com/rspec/rspec-expectations/blob/3b8d6a55d75d02039f3b95555690d9f987da1f82/lib/rspec/matchers.rb#L677) quindi non è necessario definirlo. Comporre i matcher è supportato da RSpec 3.0 –

5

Utilizzando RSpec 3 expect syntax con matchers composing:

per abbinare tutti:

expect(@user.errors[:password]).to all(match /some message/) 

per adattarsi a qualsiasi:

expect(@user.errors[:password]).to include(match /some message/) 
expect(@user.errors[:password]).to include a_string_matching /some message/ 
+0

Non è del tutto corretto. Voglio ** almeno un ** degli errori password per abbinare la stringa, ma non necessariamente ** tutti **. – Thilo

+0

Il mio male. Quindi 'expect (@ user.errors [: password]). Per includere (match/some message /)' o anche meglio 'expect (@ user.errors [: password]). Per includere a_string_matching/qualche messaggio /' –

Problemi correlati