15

Tôi có một mô hình ActiveRecord, PricePackage. Điều đó có một cuộc gọi before_create trở lại. Cuộc gọi lại này sử dụng API của bên thứ ba để thực hiện kết nối từ xa. Tôi đang sử dụng cô gái nhà máy và muốn loại bỏ api này để khi các nhà máy mới được xây dựng trong khi thử nghiệm các cuộc gọi từ xa không được thực hiện.Làm thế nào để mô phỏng và sơ khai hồ sơ hoạt động before_create gọi lại với factory_girl

Tôi đang sử dụng Rspec cho mocks và cuống. Vấn đề tôi đang gặp là các phương pháp RSpec không có sẵn trong factories.rb tôi

mô hình:

class PricePackage < ActiveRecord::Base 
    has_many :users 
    before_create :register_with_3rdparty 

    attr_accessible :price, :price_in_dollars, :price_in_cents, :title 


    def register_with_3rdparty 
     return true if self.price.nil? 

     begin 
      3rdPartyClass::Plan.create(
      :amount => self.price_in_cents, 
      :interval => 'month', 
      :name => "#{::Rails.env} Item #{self.title}", 
      :currency => 'usd', 
      :id => self.title) 
     rescue Exception => ex 
      puts "stripe exception #{self.title} #{ex}, using existing price" 
      plan = 3rdPartyClass::Plan.retrieve(self.title) 
      self.price_in_cents = plan.amount 
      return true 
     end 
    end 

nhà máy:

#PricePackage 
Factory.define :price_package do |f| 
    f.title "test_package" 
    f.price_in_cents "500" 
    f.max_domains "20" 
    f.max_users "4" 
    f.max_apps "10" 
    f.after_build do |pp| 
    # 
    #heres where would like to mock out the 3rd party response 
    # 
    3rd_party = mock() 
    3rd_party.stub!(:amount).price_in_cents 
    3rdPartyClass::Plan.stub!(:create).and_return(3rd_party) 
    end 
end 

Tôi không chắc chắn làm thế nào để có được rspec giả lập và sơ đồ giúp đỡ nạp vào factories.rb của tôi và điều này có thể không phải là cách tốt nhất để xử lý này.

+1

là một sang một bên, khi bạn gán một tiền thưởng cho một câu hỏi tiền thưởng sẽ được thực hiện từ danh tiếng của bạn cho dù bạn gán nó là một điều tốt đẹp để làm cho f ollow qua nó và phân bổ nó cho một trong những câu trả lời mà mọi người đưa ra. Nếu không làm điều đó nó đơn giản bốc hơi –

+0

Liệu 'pp.stub (: register_with_3rdparty) {true}' trong 'after_build' có gây ra lỗi nào không? – lulalala

Trả lời

19

Là tác giả của đá quý VCR, bạn có thể mong đợi tôi giới thiệu nó cho các trường hợp như thế này.Tôi thực sự khuyên bạn nên thử nghiệm mã HTTP phụ thuộc, nhưng tôi nghĩ rằng có một vấn đề cơ bản với thiết kế của bạn. Đừng quên rằng TDD (phát triển theo hướng thử nghiệm) có nghĩa là một kỷ luật thiết kế, và khi bạn cảm thấy đau đớn để dễ dàng kiểm tra một cái gì đó, đó là nói cho bạn điều gì đó về thiết kế của bạn. Lắng nghe nỗi đau của bạn!

Trong trường hợp này, tôi nghĩ rằng mô hình của bạn không có doanh nghiệp thực hiện cuộc gọi API của bên thứ ba. Đó là một sự vi phạm khá đáng kể về nguyên tắc trách nhiệm duy nhất. Mô hình phải chịu trách nhiệm về việc xác thực và kiên trì của một số dữ liệu, nhưng điều này chắc chắn vượt ra khỏi đó.

Thay vào đó, tôi khuyên bạn nên chuyển cuộc gọi API của bên thứ ba thành người quan sát. Pat Maddox có một số great blog post thảo luận cách các nhà quan sát có thể (và nên) được sử dụng để ghép nối một cách lỏng lẻo mà không vi phạm SRP (nguyên tắc trách nhiệm duy nhất) và cách thực hiện kiểm tra, dễ dàng hơn nhiều và cải thiện thiết kế của bạn.

Khi bạn đã chuyển nó vào một người quan sát, thật dễ dàng vô hiệu hóa người quan sát trong các bài kiểm tra đơn vị của bạn (ngoại trừ các bài kiểm tra cụ thể cho người quan sát đó), nhưng giữ nó trong sản xuất và trong các bài kiểm tra tích hợp của bạn. Bạn có thể sử dụng plugin no-peeping-toms của Pat để trợ giúp việc này, hoặc nếu bạn đang ở trên đường ray 3.1, bạn nên kiểm tra new functionality được tích hợp sẵn trong ActiveModel cho phép bạn easily enable/disable observers.

+0

Điều gì nếu cuộc gọi API * là * sự bền bỉ (giống như những gì ActiveResource làm)? Có vẻ như không phù hợp với điều đó * không * trong mô hình. –

+0

Chắc chắn, nếu dữ liệu đang được lưu giữ trên API HTTP, thì có, đó sẽ là trách nhiệm chính của mô hình và hoàn toàn nằm trong mô hình (hoặc trong siêu lớp hoặc mô-đun được trộn vào mô hình). Lưu ý rằng tôi đã nói "Cuộc gọi API của bên thứ ba". Tôi sẽ không bao giờ xem xét sự kiên trì của bạn để trở thành một phần API thứ 3. –

+0

Phải, tôi hiểu ý của bạn. Cảm ơn bạn đã làm rõ. –

-1

Vâng, đầu tiên, bạn nói đúng đó là giả và còn sơ khai 'không phải là ngôn ngữ của Factory Girl

đoán tại mối quan hệ mô hình của bạn, tôi nghĩ rằng bạn sẽ muốn xây dựng một nhà máy đối tượng, thiết lập thuộc tính của nó và sau đó liên kết chúng.

#PricePackage 
Factory.define :price_package do |f| 
    f.title "test_package" 
    f.price_in_cents "500" 
    f.max_domains "20" 
    f.max_users "4" 
    f.max_apps "10" 
    f.after_build do |pp| 
    f.3rdClass { Factory(:3rd_party) } 
end 

Factory.define :3rd_party do |tp| 
    tp.price_in_cents = 1000 
end 

Hy vọng rằng tôi không làm xáo trộn mối quan hệ một cách bất hợp pháp.

+0

Không có kết hợp dữ liệu giữa 'price_package' và nội dung của bên thứ ba. Tôi đã thêm một ví dụ về mô hình của tôi. Điều này giúp chứng minh rằng api của bên thứ ba được gọi trong đường dẫn 'before_create' gọi lại. Vì vậy, tôi muốn khai thác và giả định phần đó trong phương thức register_with_3rdparty. Vì vậy, cô gái nhà máy không kết nối trực tiếp mỗi khi một nhà máy 'price_package' mới được tạo ra. – kevzettler

1

Thanh toán đá quý VCR (https://www.relishapp.com/myronmarston/vcr). Nó sẽ ghi lại các tương tác HTTP của bộ thử nghiệm của bạn và phát lại chúng cho bạn. Xóa mọi yêu cầu để thực sự tạo kết nối HTTP cho API của bên thứ ba. Tôi đã tìm thấy đây là một cách tiếp cận đơn giản hơn nhiều so với chế nhạo tương tác bằng tay. Đây là một ví dụ sử dụng thư viện Foursquare.

VCR.config do |c| 
    c.cassette_library_dir = 'test/cassettes' 
    c.stub_with :faraday 
end 

describe Checkin do 
    it 'must check you in to a location' do 
    VCR.use_cassette('foursquare_checkin') do 
     Skittles.checkin('abcd1234') # Doesn't actually make any HTTP calls. 
            # Just plays back the foursquare_checkin VCR 
            # cassette. 
    end 
    end 
end 
0

FactoryGirl có thể còn sơ khai ra các thuộc tính của một đối tượng, có lẽ có thể giúp bạn:

# Returns an object with all defined attributes stubbed out 
stub = FactoryGirl.build_stubbed(:user) 

Bạn có thể tìm thêm thông tin trong FactoryGirl's rdocs

1

Mặc dù tôi có thể thấy sự hấp dẫn về đóng gói, việc bên thứ ba đang cố gắng không phải xảy ra (và trong một số cách có lẽ không nên xảy ra) trong nhà máy của bạn.

Thay vì đóng gói nó trong nhà máy, bạn có thể chỉ cần xác định nó khi bắt đầu kiểm tra RSpec của bạn. Thực hiện điều này cũng đảm bảo rằng các giả định của các bài kiểm tra của bạn rõ ràng và được nêu ngay từ đầu (có thể rất hữu ích khi gỡ lỗi)

Trước bất kỳ thử nghiệm nào sử dụng PricePlan, hãy thiết lập phản hồi mong muốn và sau đó trả lại từ bên thứ 3 .create phương pháp:

before(:all) do 
    3rd_party = mock('ThirdParty') 
    3rdPartyClass::Plan.stub(:create).and_return(true) 
end 

Điều này sẽ cho phép bạn gọi phương thức nhưng sẽ khởi động cuộc gọi từ xa.

* Có vẻ như bên thứ ba của bạn có một số phụ thuộc vào đối tượng ban đầu (: price_in_cents) tuy nhiên không biết thêm về sự phụ thuộc chính xác tôi không thể đoán được điều gì sẽ phù hợp (hoặc nếu cần) *

+0

dường như không hoạt động: TypeError: # không phải là lớp/mô-đun – avioing

+0

đây là một vấn đề khác với cách tiếp cận này ... thông thường, tôi hết lòng đồng ý WRT giữ kiểm tra sạch sẽ. trong trường hợp này, tuy nhiên, vì mô hình có thể được sử dụng - trực tiếp hoặc gián tiếp - trong nhiều bài kiểm tra, bạn sẽ phải làm điều này trong mọi thử nghiệm đơn lẻ ... do đó @kevzettler tìm kiếm gói gọn (bên trong nhà máy) – avioing

0

Tôi có cùng một vấn đề chính xác. Thảo luận quan sát sang một bên (có thể là cách tiếp cận phải), đây là những gì đã hiệu quả đối với tôi (đó là sự khởi đầu và có thể/nên được cải thiện):

thêm một tập tin 3rdparty.rb để xác định/hỗ trợ với những nội dung này :

RSpec.configure do |config| 
    config.before do 
    stub(3rdPartyClass::Plan).create do 
    [add stuff here] 
    end 
    end 
end 

Và hãy chắc chắn rằng bạn có spec_helper.rb này:

Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 
Các vấn đề liên quan