2016-05-07 2 views
2

Я хотел бы определить фабрику для отношений has_many/has_many (что, я думаю, я понял это правильно), но я не знаю, как тогда определить атрибуты для каждого объекта созданной фабрики.Rails 4/Factory Girl - has_many/has_many factory и определение разных объектов

Вот домашняя страница, это список карт сделок

Мы предполагаем, что вид домашний ниже для зарегистрированны пользователя (примечание current_user происходит от Завещания).

<% @deals.each do |deal| %> 
    @userdeal = UserDeal.where('user_id = ? AND deal_id = ?', current_user.id, deal.id).take 

    <div> 
     <div class="button"> 
     <% if @userdeal.number_of_clicks = 4 %>you reached the maximum clicks 
     <% end %> 
     </div> 
    </div> 
    <% end %> 

В принципе, если пользователь имеет для конкретной сделки (на столе user_deals см ниже модели) а user_deal.number_of_clicks = 4, то на карте появится кнопка с сообщением вроде «вы Достигнуто максимальные клики ". если nb кликов < 4, кнопка не появляется.

Так что я хочу использовать фабрику в тесте Feature, где я проверю, что если я создам с девушкой-жиркой один объект @ userdeal1, где пользователь достиг 4 кликов, на этой карте он видит кнопку и ее текст, но для других сделок он ничего не видит.

Вот что я до сих пор

модели

class Deal < ActiveRecord::Base 
    has_many :user_deals,   dependent: :destroy 
    has_many :users,    through: :user_deals 
end 

class User < ActiveRecord::Base 
    has_many :user_deals   
    has_many :deals,    through: :user_deals 
end 

class UserDeal < ActiveRecord::Base 
    belongs_to :user,   :foreign_key => 'user_id' 
    belongs_to :deal,   :foreign_key => 'deal_id' 
end 

и структура таблицы user_deal

# Table name: user_deals 
# 
# id     :integer   not null, primary key 
# user_id   :integer 
# deal_id   :integer 
# number_of_clicks :integer   default(0) 
# created_at   :datetime 
# updated_at   :datetime 

До сих пор я нашел, как создать

FactoryGirl.define do 
    factory :user_deal do 
    association :user 
    association :deal 

end 

После факто rygirl ReadMe Я создал это:

FactoryGirl.define do 
    factory :user do 

    sequence(:email) { |n| "person#{n}@example.com"} 
    password "vddfdf" 
    password_confirmation "vddfdf" 
     confirmed_at Time.now 
     confirmation_token nil 

    factory :superadmin do 
     after(:create) {|user| user.add_role(:superadmin)} 
    end 

    after(:create) do |user| 
     user.deals << FactoryGirl.create(:deal) 
    end 

    factory :user_with_deals do 
     # used for defining user_deals 
     transient do 
     deals_count 5 
     end 
     after(:create) do |user, evaluator| 
     create_list(:deal, evaluator.deals_count, user: user) 
     end 
    end 

    end 
end 

Но теперь я не знаю, как сказать «КИ одного из этих созданных user_deals на заводе, я хотел бы иметь один NUMBER_OF_CLICKS = 4, а другие, NUMBER_OF_CLICKS = 1). поэтому я попытался поставить это внутри теста непосредственно, как показано ниже:

describe 'HP deal card features', :type => :feature do 

    context "As signed-in USER" do 
    let(:subject) { ApplicationController.new } 

    before do  
     @user = FactoryGirl.create(:user, :user_country_name => 'Germany') 
     @deal1 = FactoryGirl.build(:deal) 
     @deal2 = FactoryGirl.build(:deal) 
     @user_deal_with_max_of_clicks = FactoryGirl.build(:user_with_deals, 
                  :user    => @user, 
                  :deal    => @deal1, 
:number_of_clicks => 4 
                 ).save(validate: false) 
     @user_deal_with_clicks_available = FactoryGirl.build(:user_with_deals, 
                  :user    => @user, 
                  :deal    => @deal2, 
number_of_clicks => 1 # still has clicks 
                 ).save(validate: false) 
    it "the right behavior for the buttons telling him how many clicks he has left" do 

     sign_in @user 
     visit root_path 
     # I'll find a way to test here if there is the text "you reached the maximum clicks" for the first card and not for the second 
    end 
    after do 
     visit destroy_user_session_path 
    end 

    end 

Но тест не работает, и дайте мне другой тип ошибок в соответствии с небольшим изменением, я стараюсь. Я уверен, что мне не удается действительно создать 2 объекта user_deals, один с number_of_clicks = 4, а другой - number_of_clicks = 1.

EDIT

После просьбы создавать ошибки:

Если я оставить внутри @user_deal_with_max_of_clicks ": пользователя => @user и: дело => @ deal1", я получаю

NoMethodError: 
     undefined method `user=' for #<User:0x0000000b4754e0> 

и

NoMethodError: 
     undefined method `deal=' for #<User:0x0000000b451568> 

Но я удалить их следующим образом:

@user_deal_with_max_of_clicks = FactoryGirl.build(:user_with_deals,                
    :number_of_clicks => 4 
                 ).save(validate: false) 

тогда я получаю эту ошибку:

NoMethodError: 
     undefined method `number_of_clicks=' for #<User:0x0000000b46ce80> 

EDIT 2

utilities.rb

include ApplicationHelper 

def sign_in(user) 
    visit new_user_session_path 
    fill_in  I18n.t("formtastic.labels.user.email"),   with: user.email 
    fill_in  I18n.t("formtastic.labels.user.password"),  with: user.password 
    click_on I18n.t("devise.sessions.login_page.login") 
end 
+0

Может вы публикуете ошибки, которые он вам дает? – mysmallidea

+0

@mysmallidea см. Редактировать – Mathieu

ответ

2

Во-первых, я 'd советую использовать let. Существует большое руководство для написания cleaner and better specs.

Во-вторых, важно создать хорошие заводы. Они упрощают процесс тестирования непомерно:

Так, начиная от, ваши фабрики пользователя могут быть перестроены в

FactoryGirl.define do 
    factory :user do 
    sequence(:email) { |n| "person#{n}@example.com" } 

    password "vddfdf" 
    password_confirmation "vddfdf" 

    confirmed_at Time.now 
    confirmation_token nil 

    trait :superadmin do 
     role :superadmin 
    end 

    trait :with_deals do 
     after(:create) do |user| 
     create_list(:deal, 5, user: user) 
     end 
    end 
    end 
end 

Traits являются хорошим способом выборочно регулировать фабрики. Так что если вы хотите, чтобы пользователь, чтобы быть SuperAdmin, теперь просто использовать

create(:user, :superadmin) 

Хотят SuperAdmin с сделками? Легко.

create(:user, :superadmin, :with_deals) 

Вы не разместили свои фабрики сделок, но я уверен, что вы можете адаптировать эти трюки к ним.

И наконец, приводя к user_deals. На ваших текущих заводах вы не обращаетесь к своему столбцу number_of_clicks.

Опять же, вы можете легко установить это с чертами:

FactoryGirl.define do 
    factory :user_deal do 
    association :user 
    association :deal 

    trait :few_clicks do 
     number_of_clicks 1 
    end 

    trait :many_clicks do 
     number_of_clicks 4 
    end 
    end 
end 

Теперь к самой спецификации, с учеными трюков, это легкая задача создать желаемые отношения:

describe 'HP deal card features', :type => :feature do 
    context "As signed-in USER" do 
    let(:subject) { ApplicationController.new } 
    let(:user) { create(:user, user_country_name: 'Germany') } 
    let(:deal1) { create(:deal) } 
    let(:deal2) { create(:deal) } 

    before do 
     create(:user_deal, :few_clicks, user: user, deal: deal1) 
     create(:user_deal, :many_clicks, user: user, deal: deal2) 
    end 

    it "tells the user how many clicks he has left" do 
     sign_in user 
     visit root_path 
     # I'll find a way to test here if there is the text "you reached the maximum clicks" for the first card and not for the second 
    end 

    after do 
     visit destroy_user_session_path 
    end 
    end 
end 
+0

спасибо за предложение. Ясно, что мне нужно очищать вещи. Сделаю. Я сейчас испытаю ваше предложение в приложении. – Mathieu

+0

Я получаю сообщение об ошибке, и строка, о которой сообщается об ошибке, даже не внутри «она сообщает пользователю, сколько кликов у него осталось», а внутри «before do»: она находится в строке «create (: user_deal, : few_clicks, user: user, deal: deal1) "=> ошибка: ActiveRecord :: AssociationTypeMismatch: Ожидаемый результат (# 86165360), получил TrueClass (# 14299980) – Mathieu

+0

Кстати могу ли я использовать save (validate: false) с вашим способом создания вещей (let (: deal1) {create (: deal)} действительно нужно пропустить жесткие проверки. Именно поэтому я использовал: FactoryGirl.build() .save (validate: false) (действительно, в мое реальное приложение, я добавлял другие атрибуты, такие как даты, которые мне не нужно проверять в тестах) – Mathieu

Смежные вопросы