2012-10-23 16 views
6

Mam dwie klasy:Szybkie testy (Rspec) z i bez szyn

1. Sprzedaż jest podklasą ActiveRecord; jego zadaniem jest utrzymywanie danych dotyczących sprzedaży w bazie danych.

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 jest standardową klasą Ruby; jego zadaniem jest tworzenie i tworzenie wykresów informacji o sprzedaży.

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 

Bo chcę użyć TDD i chcę moje próby uruchomienia bardzo szybko, napisałem spec SalesReport że nie nie załadować szyn:

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 

ten test działa po uruchomieniu bundle exec rspec spec/models/report_spec.rb.

Jednak ten test kończy się niepowodzeniem po uruchomieniu bundle exec rake spec z błędem superclass mismatch for class Sale (TypeError). Wiem, że błąd występuje, ponieważ Tap jest zdefiniowany przez sale.rb i wbudowany w specyfikację.

Więc moje pytanie jest sposobem na Stub (lub makiety lub Double) klasy, jeśli ta klasa nie jest zdefiniowana? Pozwoliłoby mi to usunąć wbudowany class Sale; end, który wydaje się być hack.

Jeśli nie, w jaki sposób skonfigurować testy tak, aby działały poprawnie, niezależnie od tego, czy uruchamiam bundle exec rspec czy bundle exec rake spec?

Jeśli nie, czy moje podejście do pisania szybkich testów jest złe ?!

Wreszcie, nie chcę używać Sporka. Dzięki!

Odpowiedz

4

RSpec niedawno dodane stub_const jest zaprojektowane specjalnie dla takich przypadków:

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 

Możesz także chcieć użyć rspec-fire, aby użyć podwójnego testu zamiast Sale, który automatycznie sprawdza, czy wszystkie metody fałszywe/skrótowe istnieją w rzeczywistej klasie Sale podczas wykonywania testów z załadowaną klasą Sale (np. po uruchomieniu zestaw testowy):

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 

przypadku zmiany nazwy total_for_duration realnej Sale klasie rspec ognia daje błąd podczas mock metodę, ponieważ nie istnieje w realnym klasie.

+0

Dzięki Myron Marston! – Mike

4

Prostym sposobem byłoby sprawdzić, czy „Sale” została już zdefiniowana

unless defined?(Sale) 
    class Sale; end 
end 

sprzedaży nie musi być klasą albo w teście tak:

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

Dzięki. Wygląda obiecująco. – Mike