2012-10-23 17 views
6

Ich habe zwei Klassen:Schnelle (Rspec) Tests mit und ohne Schienen

1.Sale ist eine Unterklasse von ActiveRecord; Seine Aufgabe besteht darin, Verkaufsdaten in der Datenbank zu erhalten.

class Sale < ActiveRecord::Base 
    def self.total_for_duration(start_date, end_date) 
    self.count(conditions: {date: start_date..end_date}) 
    end 
    #... 
end 

2.SalesReport ist eine Standard-Ruby-Klasse; Seine Aufgabe besteht darin, Informationen über den Vertrieb zu erstellen und grafisch darzustellen.

class SalesReport 
    def initialize(start_date, end_date) 
    @start_date = start_date 
    @end_date = end_date 
    end 

    def sales_in_duration 
    Sale.total_for_duration(@start_date, @end_date) 
    end 
    #... 
end 

Weil ich will, TDD verwenden, und ich möchte, dass meine Tests wirklich schnell laufen, ich habe eine Spezifikation für Salesreport geschrieben, die nicht nicht Rails ist nicht geladen:

require_relative "../../app/models/sales_report.rb" 

class Sale; end 
# NOTE I have had to re-define Sale because I don't want to 
# require `sale.rb` because it would then require ActiveRecord. 

describe SalesReport do 
    describe "sales_in_duration" do 
    it "calls Sale.total_for_duration" do 
     Sale.should_receive(:total_for_duration) 
     SalesReport.new.sales_in_duration 
    end 
    end 
end 

Diese Test funktioniert wenn ich bundle exec rspec spec/models/report_spec.rb ausführen. Dieser Test schlägt fehl, wenn ich bundle exec rake spec mit dem Fehler superclass mismatch for class Sale (TypeError) starte. Ich weiß, dass der Fehler passiert, weil Tap von sale.rb definiert und innerhalb der Spezifikation inline ist.

Also meine Frage gibt es einen Weg zu Stub (oder Mock oder Double) eine Klasse, wenn diese Klasse nicht definiert ist? Dies würde mir erlauben, die Inline class Sale; end zu entfernen, die sich wie ein Hack fühlt.

Wenn nicht, wie richte ich meine Tests so ein, dass sie korrekt ausgeführt werden, unabhängig davon, ob ich bundle exec rspec oder bundle exec rake spec ausführen?

Wenn nicht, ist mein Ansatz, schnelle Tests falsch zu schreiben ?!

Schließlich möchte ich nicht Spork verwenden. Vielen Dank!

Antwort

4

RSpec kürzlich stub_const hinzugefügt ist speziell für Fälle wie diese entwickelt:

describe SalesReport do 
    before { stub_const("Sale", Class.new) } 

    describe "sales_in_duration" do 
    it "calls Sale.total_for_duration" do 
     Sale.should_receive(:total_for_duration) 
     SalesReport.new.sales_in_duration 
    end 
    end 
end 

Sie können auch 012 verwenden, um ein Testdoppel anstelle von Sale zu verwenden, das automatisch prüft, ob alle Mocked-/Stubbed-Methoden in der echten Klasse Sale vorhanden sind, wenn Sie Ihre Tests mit der echten geladenen Klasse (z. B. Sale) ausführen. wenn Sie Ihre Testsuite ausgeführt wird):

require 'rspec/fire' 

describe SalesReport do 
    include RSpec::Fire 

    describe "sales_in_duration" do 
    it "calls Sale.total_for_duration" do 
     fire_replaced_class_double("Sale") 
     Sale.should_receive(:total_for_duration) 
     SalesReport.new.sales_in_duration 
    end 
    end 
end 

Wenn Sie total_for_duration auf die Real Sale Klasse umbenennen, wird rspec Feuer Sie einen Fehler, wenn Sie die Methode spotten, da es nicht auf die Realklasse existiert.

+0

Danke Myron Marston! – Mike

4

Ein einfacher Weg wäre zu prüfen, ob "Sale" hat bereits

unless defined?(Sale) 
    class Sale; end 
end 

Verkauf braucht nicht eine Klasse entweder in Ihrem Test so definiert:

unless defined?(Sale) 
    Sale = double('Sale') 
end 
+0

Danke. Sieht vielversprechend aus. – Mike

Verwandte Themen