2016-04-15 18 views
3

Stellen haben wir folgende Stück Code:Wie vermeidet man die Verwendung allow_any_instance_of?

class A 
    def create_server 
    options = { 
     name: NameBuilder.new.build_name 
    } 
    do_some_operations(options) 
    end 
end 

solche Methoden zu testen, habe ich verwendet allow_any_instance_of verwenden:

it 'does operations' do 
    allow_any_instance_of(NameBuilder).to receive(:build_name) 
    # test body 
end 

Aber docs raten uns nicht because of several reasons zu verwenden. Wie dann vermeiden Sie allow_any_instance_of? Ich habe kam nur eine Lösung:

class A 
    def create_server 
    options = { 
     name: builder.build_name 
    } 
    do_some_operations 
    end 

    private 

    def builder 
    NameBuilder.new 
    end 
end 

Aber mit einem solchen Ansatz Code wird schnell voll von fast nutzlos Methoden (vor allem, wenn Sie Zusammensetzung von verschiedenen Objekten in beschrieben Klasse aktiv verwendet wird).

Antwort

3

Wenn es schwierig ist, zu testen, bedeutet dies, dass Sie ein Problem in Ihrem Klassendesign haben. In Ihrem Fall, wenn Sie mit dem Testen für bestimmte Methodenaufruf auf einer bestimmten Klasse innerhalb einer Klasse tun testen Sie wie folgt aus:

allow_any_instance_of(NameBuilder).to receive(:build_name) 

Ihr Test genau wissen, wie das Verfahren intern implementiert ist. Ihre Klassen sollten die Logik kapseln und verstecken. Sie tun genau das Gegenteil.

Sie sollten nicht testen jede interne Methodenlogik. Testen Sie einfach das Verhalten. Geben Sie Eingaben und testen Sie die Richtigkeit der Ausgabe.

Wenn Sie diese Methodenaufruf auf NameBuilder Klasse wirklich testen möchten, dann injizieren Sie diese Abhängigkeit und machen Sie Ihre Klasse mehr testbar. Dies entspricht auch den OOP-Prinzipien.

class A 
    def create_server(builder) 
    do_some_operations(name: builder.build_name) 
    end 
end 
+0

Und dann sollten wir Doppel verwenden, um Builder-Objekt zu fälschen, oder? – sekrett

3

In Abwesenheit von Dependency Injection nach Uzbekjon's answer (was ich zustimme) Sie auch den Aufruf von NameBuilder.new Ausdrücken in Erwägung ziehen könnten, so können Sie die direkte Kontrolle über die Instanz von NameBuilder im Test haben:

class NameBuilder 
    def build_name 
    # builds name... 
    end 
end 

class A 
    def create_server 
    options = { 
     name: NameBuilder.new.build_name 
    } 
    do_some_operations(options) 
    end 

    def do_some_operations(options) 
    # does some operations 
    end 
end 

RSpec.describe A do 
    let(:a) { described_class.new } 

    describe '#create_server' do 
    let(:name_builder) { instance_double(NameBuilder) } 

    before do 
     allow(NameBuilder).to receive(:new).and_return(name_builder) 
    end 

    it 'does operations' do 
     # the first expectation isn't really part of what you seem 
     # to want to test, but it shows that this way of testing can work 
     expect(name_builder).to receive(:build_name) 
     expect(a).to receive(:do_some_operations) 
     a.create_server 
    end 
    end 
end 
+0

Ja, dazu bin ich schließlich gekommen. Es ist besonders praktisch, wenn mehrere Objekte in einem Zyklus erstellt werden. – hedgesky

Verwandte Themen