2013-07-29 10 views
6

Posso sapere come eseguire il metodo di stub nel metodo di creazione del controller? Ho bisogno di scrivere le specifiche per questo, ma ho ricevuto questi errori. Ho bisogno di controllare il metodo di creazione nel controller deve eseguire il metodo validate_fbid prima di creare un nuovo record aziendale nel modello.Rspec: metodo di stub nel controller

Errore:

1) Companies new company create with valid information#validate_fbid should have correct parameters and return value 
Failure/Error: CompaniesController.create.should_receive(:validates_fbid).with(company) 
NoMethodError: 
    undefined method `create' for CompaniesController:Class 
# ./spec/requests/companies_spec.rb:38:in `block (5 levels) in <top (required)>' 

    2) Companies new company create with valid information#validate_fbid should fbid validation passed 
Failure/Error: CompaniesController.create.stub(:validates_fbid).and_return('companyid') 
NoMethodError: 
    undefined method `create' for CompaniesController:Class 
# ./spec/requests/companies_spec.rb:43:in `block (5 levels) in <top (required)>' 

CompaniesController

def create 
company = Company.new(params[:company]) 
verifyfbid = validate_fbid(company) 

if verifyfbid != false 
    if company.fbid.downcase == verifyfbid.downcase 
     if company.save 
      @message = "New company created." 
      redirect_to root_path 
     else 
      @message = "Company create attempt failed. Please try again." 
      render 'new' 
     end 
    else 
     @message = "Company create attempt failed. Invalid facebook id." 
     render 'new' 
    end 
else 
    @message = "Company create attempt failed. No such facebook id." 
    render 'new'    
    end    
end 

    private 
    def validate_fbid(company) 
    uri = URI("http://graph.facebook.com/" + company.fbid) 
    data = Net::HTTP.get(uri) 
    username = JSON.parse(data)['username']  
    if username.nil? 
    return false 
    else 
    "#{username}" 
    end 
end 

Richieste/companies_spec.rb

context "#validate_fbid" do    
     #validate fbid 
     let(:company){ Company.new(name:'Example Company', url: 'www.company.com', fbid: 'companyid', desc: 'Company desc')} 

     it "should have correct parameters and return value" do 
      CompaniesController.create.should_receive(:validates_fbid).with(company) 
           .and_return('companyid') 
     end 

     it "should fbid validation passed" do    
      CompaniesController.create.stub(:validates_fbid).and_return('companyid') 
      company.fbid.should_not be_nil 
      company.fbid.should == 'companyid' 
      company.save 
      expect { click_button submit }.to change(Company, :count).by(1) 
     end            
    end  

risposta

15

Non si vuole a stub il metodo, quando è l'oggetto del test case

context "#validate_fbid" do 
    #test the function here 
    #don't stub 
end 

quando si prova l'azione creare nel controller, è possibile stub "validate_fbid"

describe "post create" do 
    ... 
    CompaniesController.any_instance.stub(:validates_fbid).and_return('companyid') 
    ... 
end 

Speranza che aiuta.

+10

'allow_any_instance_of (CompaniesController) .to ricezione (: validates_fbid).and_return ('companyid') 'per Rspec3 – ryan2johnson9

5

Quando il codice è difficile da testare, di solito è perché è complesso.

Si dovrebbe refactoring questo codice in questo modo:

  • mossa logica di verifica nella nuova 'classe di servizio', che ha un unico responsabile della verifica azienda su facebook
  • questo renderà la funzionalità verifica indipendente dello strato web e molto più facile per testare
  • make spec per la classe di servizio, che metterà alla prova questo codice in isolamento (nessun controller)
  • pulizia del controllore della logica - si don' t vogliono avere la logica all'interno del vostro controller (regola empirica: un livello di nidificazione max)
  • spec per il controller sarà più facile così

Il codice regolatore può essere simile a questa:

def create 
    company = Company.new(params[:company]) 
    verified = FbCompanyVerifier.new.verify(company) 

    if verified and company.save 
    # success logic 
    else 
    # fail logic 
    end 
end 
+0

Ciao, grazie per la risposta. Significa che ho bisogno di creare una classe FbCompanyVerifier che abbia anche il "nuovo" metodo e il metodo "verifica"? Suppongo che non mi sia consentito creare una classe nella pagina del controller, quindi quale cartella dovrei creare "FbCompanyVerifier"? Grazie per il chiarimento. Sono molto nuovo a questo. –

+0

Corretto: una classe completa con il metodo di istanza "verifica" ed è possibile saltare il "nuovo" metodo (inizializza) se non esegue nulla. Di solito questa è una best practice in modo da operare con oggetti di prima classe/oggetti ruby ​​semplici. (Alcuni potrebbero usare la classe con metodi di classe, che ha i suoi pro e contras). Poiché questa classe dipende dal modello aziendale, è necessario inserirla in qualche parte nella cartella dell'app "app/services". Puoi inserire piccole classi di responsabilità singola, che contengono un metodo o due, e sono indipendenti dal livello web. – jurglic

+0

Puoi anche controllare il post del blog code_climate, che descrive un simile refactoring: estraendo il codice logico in una nuova classe con responsabilità singola, scrivi semplici specifiche, e poi usalo come un PORO (plain-old-ruby-object) nel codice ... : http://blog.codeclimate.com/blog/2013/07/23/testing-code-in-a-rails-initializer/ – jurglic

1

Se si esegue il test di controllo, è possibile accedere direttamente regolatore:

controller.stub(:message) { 'this is the value to return' } 
+0

Questa risposta funziona per me perché quando stubo il metodo, so che accetta una discussione, ma non so quale sia l'argomento. Voglio solo passare l'argomento, quindi uso un blocco con un argomento per sovrascrivere (stub) il metodo. – Volte