Правильно ли я использую фабрики?

Это моя текущая тестовая установка:

# spec/factories.rb
require 'factory_girl'

FactoryGirl.define do
  # Roles

  factory :user_role, :class => Role do
    name 'User'
  end

  # Users
  factory :user, :class => User do
    sequence(:email) {|n| "email#{n}@example.com" }
    password 'password'
    password_confirmation 'password'
    name 'Yuri Userington'
    roles { |a| [a.association(:user_role)] }
  end

  # Instruments
  factory :instrument, :class => Instrument do
    title "Doobie Doo Instrument Title"
    is_valid true
    association :user, :factory => :user
  end

  # Sequences
  sequence :email do
    "email#{n}@factory.com"
  end

end

# spec/controllers/instruments_controller_spec.rb
require 'spec_helper'

describe InstrumentsController do

  before (:each) do
    @instrument = FactoryGirl.create(:instrument)
    @attr = FactoryGirl.attributes_for(:instrument)
    @user = FactoryGirl.create(:user)
  end

  describe "GET index" do
    it "assigns all instruments as @instruments" do
      instrument = Instrument.new(@attr)
      instrument.user = @user 
      instrument.save!
      get :index
      assigns(:instruments).should eq([instrument])
    end 
  end

end

В результате, когда я запускаю свои тесты, я получаю следующие ошибки в своем выводе:

Failures:

  1) InstrumentsController GET index assigns all instruments as @instruments
     Failure/Error: @instrument = FactoryGirl.create(:instrument)
     ActiveRecord::RecordNotFound:
       Couldn't find Role with id=2
     # ./app/models/user.rb:21:in `assign_role_after_sign_up'
     # ./spec/controllers/instruments_controller_spec.rb:24:in `block (2 levels) in <top (required)>'

Исходя из этого, кажется, что вызов ассоциации ролей в моей :user factory НЕ вызывается - что я здесь делаю неправильно? Я использую это совершенно неправильно?

Спасибо!!


person Mario Zigliotto    schedule 05.08.2011    source источник


Ответы (2)


Здесь можно многое сказать. Сравните свой код со следующим, чтобы увидеть, сколько строк или слов было удалено.

FactoryGirl.define do
  # Sequences
  sequence :email do |n|
    "email#{n}@factory.com"
  end

  # Roles
  factory :user_role, :class => Role do
    name 'User'
  end

  # Users
  factory :user do
    email
    password 'password'
    password_confirmation 'password'
    name 'Yuri Userington'
    roles { |user| [Factory(:user_role)] } #many to many
   end

  # Instruments
  factory :instrument, :class => Instrument do
    title "Doobie Doo Instrument Title"
    is_valid true
    association :user #one-to-one or one-to-many
  end

end

И в ваших тестах:

describe InstrumentsController do

  before (:each) do
    @user = Factory(:user)
  end

  describe "GET index" do
    it "assigns all instruments as @instruments" do
      instrument = Factory(:instrument, :user => @user)
      get :index
      assigns(:instruments).should eq([instrument])
    end 
  end

end

Кроме того:

  • Лично я предпочитаю тестировать контроллер с моками и заглушками.

  • Я использую let вместо переменных экземпляра и before_filter

person apneadiving    schedule 05.08.2011
comment
Поподробней по тому пункту? В частности, почему оба ваших предпочтения? Кроме того, большое спасибо за подробные примеры ответов/кода. - person Mario Zigliotto; 06.08.2011
comment
1) Тестирование заключается в выявлении потенциальных проблем, поэтому я использую только реальные объекты в интеграционном тестировании. 2) отличные объяснения здесь: let 3) Спасибо обычно означает +1 на stackoverflow :) - person apneadiving; 06.08.2011
comment
Одно замечание по этому поводу: roles { |user| [Factory(:user_role)] } создаст запись UserRole, даже если вы создаете только User. Использование обратного вызова after_create этого не делает. Ни один из них не является правильным или неправильным, все зависит от того, как вы собираетесь использовать свою фабрику. Это просто то, что нужно знать. - person Wizard of Ogz; 06.08.2011
comment
@apneadiving р.е. используя макеты/заглушки вместо фабрик, вы нашли способ сделать тестовый код удобным для сопровождения? Я обнаружил, что в конечном итоге мне приходится заглушать множество методов, и полученный тест трудно читать, что частично уменьшает пользу, на мой взгляд. Может быть, мне просто нужно реорганизовать код в мои модели, просто интересно, есть ли у вас представление? - person Paul Russell; 20.01.2012
comment
заглушки хороши, когда вы тестируете метод, который ожидает результатов от других объектов. Это может быть немного сложнее перечитывать, но оно того стоит. На самом деле, у меня был случай, когда я полагался на Фабрику. Однажды мы решили сменить фабрику, и многие тесты сорвались только потому, что ожидаемых ранее данных больше не было. Вы должны найти свою золотую середину :) - person apneadiving; 20.01.2012

У меня были похожие проблемы, и я использовал обратный вызов для назначения таких ролей:

Factory.define :user_with_admin_role, :parent => :user do |user|
  user.after_create {|instance| instance.roles << Factory(:admin_role) }
end

Поэтому я думаю, что вы должны быть в состоянии сделать что-то вроде этого:

# Users
factory :user, :class => User do
  sequence(:email) {|n| "email#{n}@example.com" }
  password 'password'
  password_confirmation 'password'
  name 'Yuri Userington'
  after_create {|user| user.roles << Factory(:user_role) }
end

Это полностью не проверено, поэтому вам может понадобиться настроить что-то.

person Wizard of Ogz    schedule 05.08.2011
comment
Спасибо! Могу ли я увидеть пример того, как вы составили свой фактический тест, используя пример? Например describe "GET index" do - person Mario Zigliotto; 06.08.2011
comment
@SizzlePants, апнедайвинг побил меня примером. Пожалуйста, посмотрите его пример. - person Wizard of Ogz; 06.08.2011