2016-04-19 3 views
0

ich einen Teil meiner Foos Controller in ein neues Schienen-Modell die Aktion auszuführen extrahiert haben:Wie ein Schiene PORO von Controller aufgerufen testen

foos_controller.rb

class FoosController < ApplicationController 

    respond_to :js 

    def create 
    @foo = current_user.do_something(@bar) 
    actioned_bar = ActionedBar.new(@bar) 
    actioned_bar.create 
    respond_with @bar 
    end 

actioned_bar.rb

class ActionedBar 
    def initialize(bar) 
    @bar = bar 
    end 

    def create 
    if @bar.check? 
     # do something 
    end 
    end 
end 

ich habe es zuerst arbeiten, aber jetzt versuche ich die rspec Co Back-füllen Kinderwagen-Tests.

Ich werde die verschiedenen Modellmethoden testen und mache einen Funktionstest, um sicherzustellen, dass es in dieser Hinsicht ok ist, aber ich möchte einen Test hinzufügen, um sicherzustellen, dass das neue actioned_bar-Modell von den Foos aufgerufen wird Controller mit @bar.

Ich weiß in rspec können Sie das etwas receives etwas with etwas arguments testen, aber ich habe Mühe, dies zur Arbeit zu bekommen.

it "calls ActionedBar.new(bar)" do 
     bar = create(:bar) 
     expect(ActionedBar).to receive(:new) 

     xhr :post, :create, bar_id: bar.id 
    end 

Dies allerdings nicht funktioniert, die Konsole Berichte:

NoMethodError: 
    undefined method `create' for nil:NilClass 

, die, weil es diese nur seltsam tut, wenn ich expect(ActionedBar).to receive(:new) verwenden, funktioniert der Rest der Reglertests in Ordnung.

Wenn ich versuche zu tun:

it "calls ActionedBar.new(bar)" do 
     bar = create(:bar) 
     actioned_bar = ActionedBar.new(bar) 
     expect(actioned_bar).to receive(:create).with(no_args) 

     xhr :post, :create, bar_id: bar.id 
    end 

die Konsole sagt:

(#<ActionedBar:0xc8f9f74>).create(no args) 
     expected: 1 time with no arguments 
     received: 0 times with no arguments 

Wenn ich einen put in der Steuerung zu tun, während der Test ausgeführt wird; Aus irgendeinem Grund führt dieser Test dazu, dass die actioned_bar im Controller als nil ausgegeben wird, aber für alle anderen Controller-Tests in Ordnung ist.

Kann ich testen, ob ActionedBar in dieser Controller-Spezifikation aufgerufen wird?

Antwort

1

können Sie expect_any_instance_of(ActionedBar).to receive(:create) verwenden, da beispielsweise in spec und in der Steuerung verschiedener Instanzen sind.

Wenn Sie Original-Objekt verwenden möchten, können Sie expect(ActionedBar).to receive(:new).and_call_original verwenden (ohne dass #new wird nur null zurückgeben und Sie erhalten NoMethodError).

+0

Dank Maxim Khan-Magomedov, beide Tests funktionierten gut. Ich fügte auch einen zusätzlichen Test hinzu, wie max in seiner Antwort andeutet, um sicherzustellen, dass der aktivierte Balken tatsächlich ausgeführt wird. – user1116573

0

Das Kernproblem ist, dass actioned_bar in Ihrer Spezifikation nicht die gleiche Instanz von ActionedBar ist, die in Ihrem Controller ist. Daher wird die Spezifikation immer fehlschlagen.

Stattdessen müssen Sie haben ActionedBar eine doppelte Rückkehr, wenn neue aufgerufen:

it "calls ActionedBar.new(bar)" do 
    bar = create(:bar) 
    actioned_bar = instance_double("ActionedBar") 
    allow(ActionedBar).to receive(:new).and_return(actioned_bar) 
    expect(actioned_bar).to receive(:create).with(no_args) 
    xhr :post, :create, bar_id: bar.id 
end 

jedoch diese Art von Test, den ich ein Code Geruch im Allgemeinen betrachten - es ist ok zu externen Mitarbeitern zu verspotten und setzen Erwartungen, die Sie Übergeben der richtigen Nachrichten. Aber Sie sollten vielleicht überlegen, ob Sie die Details testen, wie Ihr Controller seine Arbeit macht und nicht das tatsächliche Verhalten.

Ich finde es besser, eine Spezifikation einzurichten, die die Controller-Aktion aufruft und Erwartungen setzt, wie sie beispielsweise den Datenbankstatus ändert oder wie er die Antwort beeinflusst.

+0

Danke. Ich bin mit der Antwort von Maxim Khan-Magomedov gegangen, weil ich noch kein großer Fan von Doppeln bin, aber ich habe einen zusätzlichen Test hinzugefügt, wie Sie vorgeschlagen haben, um sicherzustellen, dass die Aktion tatsächlich ausgeführt wird. – user1116573

0

Sie können eine doppelte ActionedBar einrichten, die vom Aufruf ActionedBar.new zurückgegeben wird, da sich diese Instanz von der im Controller verwendeten Instanz unterscheidet.

describe "#create" do 
    let(:actioned_bar) { double(ActionedBar) } 
    let(:bar) { double(Bar) } 

    it "calls ActionedBar.new(bar)" do 
    expect(ActionedBar).to receive(:new).with(bar).and_returns(actioned_bar) 
    expect(actioned_bar).to receive(:create) 

    xhr :post, :create, bar_id: bar.id 
    end 
end 
Verwandte Themen