2013-12-18 16 views
8

Nói rằng tôi có mô hình UserPost, người dùng has_many bài viết và bài belongs_to người dùng.Đẩy mạnh các hiệp hội trong thông số kỹ thuật mô hình với FactoryGirl - tạo vs xây dựng vs build_stubbed

Khi tôi viết một spec cho Post, bản năng đầu tiên của tôi là viết một cái gì đó như thế này:

before do 
    @user = FactoryGirl.create :user 
    @post = @user.posts.new(title: "Foo", content: "bar) 
end 

... tests for @post go here ... 

Nhưng điều này sẽ tạo ra một tài khoản mới - nhấn cơ sở dữ liệu - cho mỗi thử nghiệm duy nhất, đó là sẽ làm chậm mọi thứ. Có cách nào tốt hơn để làm điều này mà sẽ tăng tốc độ kiểm tra của tôi lên và tránh đánh DB thường xuyên?

Như tôi đã hiểu, tôi không thể sử dụng FactoryGirl.build :user vì, mặc dù nó sẽ không nhấn DB, các hiệp hội sẽ không hoạt động bình thường vì @user sẽ không có ID và vì vậy @post.user sẽ không hoạt động (nó trả nil.)

tôi có thể sử dụng FactoryGirl.build_stubbed :user đó tạo ra một "giả vẫn kiên trì" @user mà không có một ID, nhưng @post.user vẫn trả về nil. build_stubbed có lợi thế thực tế nào trên build khi tôi đang thử nghiệm những thứ liên quan đến các hiệp hội không?

Tôi cho rằng tôi có thể sử dụng build_stubbed stub @post.user để trả về @user ... có bất kỳ lý do nào có thể là ý tưởng tồi không?

Hoặc tôi chỉ nên sử dụng create và chấp nhận tốc độ truy cập?

Cách thay thế duy nhất khác mà tôi có thể nghĩ là thiết lập @user trong khối before(:all) có vẻ như là một ý tưởng tồi.

Cách tốt nhất để viết các loại thử nghiệm này theo cách gọn gàng, súc tích mà tránh tạo quá nhiều truy vấn DB là gì?

Trả lời

18

Nếu bạn không muốn thử nghiệm của bạn được đánh cơ sở dữ liệu, đây là những gì bạn sẽ phải làm.

before do 
    @user = FactoryGirl.build_stubbed :user 
    @post = FactoryGirl.build_stubbed :post 
    @user.stub(:posts).and_return([@post]) 
    @post.stub(:user).and_return(@user) 
end 

Lưu ý: Hãy cẩn thận khi sử dụng before(:all). Nó không được thực hiện trong một giao dịch. Vì vậy, bất cứ điều gì bạn tạo trong before(:all) sẽ bị bỏ lại phía sau trong cơ sở dữ liệu và có thể gây xung đột với các thử nghiệm khác

Giới thiệu FactoryGirl.build, nó xây dựng đối tượng, nhưng tạo liên kết.

Đối với ví dụ:

factory :user do 
    association posts 
end 

FactoryGirl.build(:user) #this creates posts in the database even though you are only building the parent object(user) 
15

ngắn trả lời

@user = FactoryGirl.build_stubbed(:user) 
@post = FactoryGirl.build_stubbed(:post, :user => @user) 

Điều này sẽ làm cho công việc @ post.user mà không bao giờ chạm cơ sở dữ liệu.

dài trả lời

Khuyến cáo của tôi sẽ được chờ đợi trên khối before cho đến khi bạn chắc chắn rằng bạn cần nó. Thay vào đó, hãy xây dựng dữ liệu bạn cần cho từng thử nghiệm riêng lẻ và trích xuất sao chép sang các phương pháp hoặc các nhà máy mới khi bạn tìm thấy nó.

Ngoài ra, bạn có thực sự cần tham khảo người dùng trong mọi thử nghiệm đơn lẻ không? Có @user có sẵn trong mọi thử nghiệm nói với các nhà phát triển khác rằng điều đó quan trọng ở mọi nơi.

Cuối cùng, giả sử rằng liên kết người dùng cũng được khai báo trong nhà máy đăng bài của bạn, bạn sẽ tự động nhận được post.user hoạt động khi bạn làm build_stubbed(:post).

8

Có thể dễ dàng quên sự khác biệt giữa create, buildbuild_stubbed. Dưới đây là tham khảo nhanh cho những người trong cùng một tình huống (vì trang này xếp hạng cao trong kết quả tìm kiếm).

# Returns a User instance that's not saved (does not write to DB) 
user = build(:user) 

# Returns a saved User instance (writes to DB) 
user = create(:user) 

# Returns a hash of attributes that can be used to build a User instance 
attrs = attributes_for(:user) 

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

# Passing a block to any of the methods above will yield the return object 
create(:user) do |user| 
    user.posts.create(attributes_for(:post)) 
end 

Source

1

Từ tài liệu của nhà máy cô gái, bạn có thể xác định chiến lược build cho user gắn trong post nhà máy như thế này:

factory :post do 
    association :user, factory: :user, strategy: :build 
end 

Vì vậy mà bạn có thể build một post mà không lưu user

post = build(:post) 
post.new_record?  # => true 
post.author.new_record? # => true 
Các vấn đề liên quan