Stubbing named_scope in un controller RSpec
-
08-07-2019 - |
Domanda
Non sono stato in grado di trovare nulla per una situazione come questa. Ho un modello che ha un ambito definito così definito:
class Customer < ActiveRecord::Base
# ...
named_scope :active_customers, :conditions => { :active => true }
end
e sto provando a cancellarlo nelle specifiche del mio controller:
# spec/customers_controller_spec.rb
describe CustomersController do
before(:each) do
Customer.stub_chain(:active_customers).and_return(@customers = mock([Customer]))
end
it "should retrieve a list of all customers" do
get :index
response.should be_success
Customer.should_receive(:active_customers).and_return(@customers)
end
end
Questo non funziona e non funziona, dicendo che il Cliente si aspetta che active_customers lo abbia ricevuto 0 volte. Nel mio controller effettivo per l'azione sull'indice ho @customers = Customer.active_customers
. Cosa mi manca per farlo funzionare? Purtroppo, sto scoprendo che è più semplice scrivere il codice piuttosto che pensare a un test / specifica e scriverlo dal momento che so cosa sta descrivendo la specifica, ma non come dire a RSpec cosa voglio fare.
Soluzione
Penso che ci sia un po 'di confusione quando si tratta di stub e aspettative del messaggio . Le aspettative dei messaggi sono fondamentalmente stub, in cui è possibile impostare la risposta predefinita desiderata, ma verificano anche che la chiamata sia effettuata dal codice in fase di test. Al contrario, gli stub sono solo risposte predefinite alle chiamate del metodo. Ma non mescolare uno stub con un'aspettativa di messaggio sullo stesso metodo e testare o succederanno cose brutte ...
Tornando alla tua domanda, ci sono due cose (o più?) che richiedono di specificare qui:
- Che il Controllore Clienti chiama
Cliente # active_customers
quando si fa unget
suindex
. Non importa davvero cosaCustomer # active_customers
restituisce in questa specifica. - Che il
active_customers
named_scope in effetti restituisce i clienti in cui il campoactive
ètrue
.
Penso che tu stia provando a fare il numero 1. In tal caso, rimuovi l'intero stub e imposta semplicemente le aspettative del messaggio nel tuo test:
describe CustomersController do
it "should be successful and call Customer#active_customers" do
Customer.should_receive(:active_customers)
get :index
response.should be_success
end
end
Nelle specifiche sopra non stai testando cosa restituisce. Va bene poiché questo è lo scopo della specifica (anche se la tua specifica è troppo vicina all'implementazione rispetto al comportamento, ma questo è un argomento diverso). Se vuoi che la chiamata a active_customers
restituisca qualcosa in particolare, vai avanti e aggiungi .and_returns (@whatever)
alle aspettative di quel messaggio. L'altra parte della storia è testare che active_customers
funziona come previsto (ad es .: una specifica del modello che effettua l'effettiva chiamata al DB).
Altri suggerimenti
Dovresti avere l'array intorno alla simulazione se vuoi provare a ricevere indietro un array di record del cliente in questo modo:
Customer.stub_chain(:active_customers).and_return(@customers = [mock(Customer)])
stub_chain ha funzionato al meglio per me.
Ho un controller che chiama
ExerciseLog.this_user(current_user).past.all
E sono in grado di stubare in questo modo
ExerciseLog.stub_chain(:this_user,:past).and_return(@exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])