2012-04-05 30 views
64

Tôi đang cố gắng viết một bài kiểm tra mô phỏng một số giá trị trả về từ dịch vụ REST của Dropbox cung cấp cho tôi dữ liệu trong một mảng, với một băm lồng nhau.Làm thế nào để xác định một mảng/băm trong Factory Girl?

Tôi đang gặp khó khăn trong việc tìm cách viết mã Nhà máy của tôi vì kết quả trả về là một mảng có bên trong. Điều gì sẽ đến đây?

Factory.define :dropbox_hash do 
?? 
end 

Dropbox dữ liệu trông như thế này:

["/home", {"revision"=>48, "rev"=>"30054214dc", "thumb_exists"=>false, "bytes"=>0, "modified"=>"Thu, 29 Dec 2011 01:53:26 +0000", "path"=>"/Home", "is_dir"=>true, "icon"=>"folder_app", "root"=>"app_folder", "size"=>"0 bytes"}] 

Và tôi muốn một cuộc gọi nhà máy như thế này trong RSpec tôi:

Factory.create(:dropbox_hash) 
+0

Bạn có thực sự cần một nhà máy cho điều này? Tại sao không chỉ xác định phương thức trả về phản hồi mô phỏng? – zetetic

+0

Đó là những gì tôi đã làm. Nhưng tôi nghĩ điểm của các nhà máy là cô lập công cụ này. Tôi vẫn tò mò - có vẻ như Hash và Array là các lớp và điều này sẽ làm việc nếu tôi chỉ có thể nhận được cú pháp đúng. – Doug

+0

Tôi chỉ sử dụng chúng để tạo các phiên bản mô hình ActiveRecord. FactoryGirl dự định thay thế đồ đạc. Bạn có thể xem xét các phương thức trợ giúp của RSpec: https://www.relishapp.com/rspec/rspec-core/v/2-9/docs/helper-methods – zetetic

Trả lời

125

Tôi đã quan tâm đến việc làm tương tự, cũng để thử nghiệm mô hình của tôi hoạt động bằng cách sử dụng hàm băm nội dung từ API của bên thứ ba. Tôi thấy rằng bằng cách sử dụng một vài tính năng tích hợp của factory_girl, tôi đã có thể xây dựng một cách rõ ràng các loại cấu trúc dữ liệu này.

Dưới đây là một ví dụ contrived:

factory :chicken, class:Hash do 
    name "Sebastian" 
    colors ["white", "orange"] 

    favorites {{ 
     "PETC" => "http://www.petc.org" 
    }} 

    initialize_with { attributes } 
    end 

Bí quyết chính ở đây là khi bạn khai báo initialize_with, factory_girl sẽ không còn cố gắng gán các thuộc tính cho đối tượng kết quả. Nó cũng dường như bỏ qua các cửa hàng db trong trường hợp này. Vì vậy, thay vì xây dựng bất cứ điều gì phức tạp, chúng tôi chỉ trả lại hàm băm thuộc tính đã được chuẩn bị như nội dung của chúng tôi. Thì đấy.

Có vẻ như cần thiết để chỉ định một số giá trị cho lớp, mặc dù nó không thực sự được sử dụng. Điều này nhằm ngăn factory_girl cố gắng khởi tạo một lớp dựa trên tên nhà máy. Tôi đã chọn sử dụng các lớp mô tả thay vì Object, nhưng nó tùy thuộc vào bạn.

Bạn vẫn có thể ghi đè lên các lĩnh vực khi bạn sử dụng một trong những nhà máy băm:

chick = FactoryGirl.build(:chicken, name:"Charles") 

..however, nội dung nếu bạn đã lồng nhau và muốn ghi đè lên các lĩnh vực sâu hơn, bạn sẽ cần phải tăng độ phức tạp của khối khởi tạo để thực hiện một số loại hợp nhất sâu.

Trong trường hợp của bạn, bạn đang sử dụng một số mảng hỗn hợp và dữ liệu băm và có vẻ như thuộc tính Đường dẫn sẽ được sử dụng lại giữa các phần của cấu trúc dữ liệu. Không có vấn đề gì - bạn biết cấu trúc của nội dung, vì vậy bạn có thể dễ dàng tạo một nhà máy xây dựng mảng kết quả một cách chính xác. Dưới đây là cách tôi có thể làm điều đó:

factory :dropbox_hash, class:Array do 
    path "/home" 
    revision 48 
    rev "30054214dc" 
    thumb_exists false 
    bytes 0 
    modified { 3.days.ago } 
    is_dir true 
    icon "folder_app" 
    root "app_folder" 
    size "0 bytes" 

    initialize_with { [ attributes[:path], attributes ] } 
    end 

    FactoryGirl.build(:dropbox_hash, path:"/Chickens", is_dir:false) 

Bạn vẫn được tự do bỏ qua các giá trị không cần thiết. Hãy tưởng tượng chỉ đường dẫn và rev là thực sự cần thiết:

factory :dropbox_hash, class:Array do 
    path "/home" 
    rev "30054214dc" 
    initialize_with { [ attributes[:path], attributes ] } 
    end 

    FactoryGirl.build(:dropbox_hash, path:"/Chickens", revision:99, modified:Time.now) 
+0

Đây là một giải pháp tuyệt vời. Nó thậm chí hoạt động với thừa kế của các nhà máy. –

4

Bạn có thể làm điều này trong các phiên bản mới nhất của factory_girl, nhưng nó khó xử bởi vì nó được thiết kế để xây dựng các đối tượng chứ không phải cấu trúc dữ liệu. Dưới đây là một ví dụ:

FactoryGirl.define do 
    factory :dropbox_hash, :class => 'Object' do 
    ignore do 
     url { "/home" } 
     revision { 48 } 
     rev { "30054214dc" } 
     # more attributes 
    end 
    initialize_with { [url, { "revision" => revision, "rev" => rev, ... }] } 
    to_create {} 
    end 
end 

Đi qua những thứ kỳ lạ ở đây:

  • Mỗi nhà máy cần xây dựng lớp học có hiệu lực ngay cả khi nó không được sử dụng, vì vậy tôi đã thông qua Object đây để ngăn chặn nó từ tìm kiếm DropboxHash.
  • Bạn cần bỏ qua tất cả các thuộc tính bằng cách sử dụng khối ignore để nó không cố gán chúng cho mảng sau đó, chẳng hạn như array.revision = 48.
  • Bạn có thể cho biết cách đặt kết quả của mình lại với nhau bằng cách sử dụng initialize_with. Nhược điểm ở đây là bạn cần phải viết lại toàn bộ danh sách thuộc tính.
  • Bạn cần cung cấp một khối trống to_create để nó không cố gắng gọi array.save! sau đó.
-1

tôi đã sử dụng OpenStruct:

factory :factory_hash, class:OpenStruct do 
    foo "bar" 
    si "flar" 
end 

Edit: xin lỗi, không hoạt động như một Hash

cuối cùng tôi sử dụng một phiên bản tĩnh, chỉ để giữ cho băm đó đến từ hệ thống Nhà máy ...

factory :factory_hash, class:Hash do 
    initialize_with { { 
    foo "bar" 
    si "flar" 
    } } 
end 

tìm kiếm thứ gì đó tốt hơn

6

Một followup cho RSpec phiên bản hiện tại (3,0):

Chỉ cần xác định nhà máy của bạn như bình thường và sử dụng FactoryGirl.attributes_for để nhận băm thay vì một lớp được thuyết minh.

4

đã làm việc này đối với tôi, và tôi có thể vượt qua các thuộc tính khi cần thiết vào băm

factory :some_name, class:Hash do 
    defaults = { 
    foo: "bar", 
    baz: "baff" 
    } 
    initialize_with{ defaults.merge(attributes) } 
end 

> build :some_name, foo: "foobar" #will give you 
> { foo: "foobar", baz: "baff" } 
+1

Điều này cũng sẽ có thêm lợi ích của việc cho phép các khóa là chuỗi. – crftr

Các vấn đề liên quan