Где создавать экземпляры родительской модели в функциональном тесте (капибара)? - PullRequest
0 голосов
/ 23 октября 2018

Я тестирую создание продукта с помощью Capybara, т.е. я заполняю форму с помощью автоматического теста.Этот продукт относится к определенным моделям, например, к дому.

У меня есть два заводских файла для создания этих двух моделей: продукта и дома.В форме пользователь должен выбрать дом из выбора (выпадающий).Мне удается это сделать, но решение кажется не чистым:

(я создаю домашний экземпляр в тесте возможностей, поскольку мне нужно выбрать дом в форме для продукта. Этот дом принадлежитдругие модели)

require 'rails_helper'
require 'pry'

RSpec.describe 'Add a product features' do

  context "Create new product from add a product menu" do
    let(:user) { create(:user) }
    let!(:home) { create(:home, name: "My Place", user: user) }

    before(:each) do
      # home.name = "My place"
      # home.save
    end

    before(:each) do
      # binding.pry
      login_as(user, :scope => :user)
      visit menu_add_product_path

      click_link("Take a picture")

      expect(current_path).to eql('/products/new')
      binding.pry
      within('form') do
        attach_file('product_taken_photos_attributes_0_taken_pic', File.absolute_path('./app/assets/images/macbook1.jpg'))
        fill_in 'Brand', with: "Apple"
        fill_in 'Product type', with: "Smartphone"
        fill_in 'Price of purchase', with: 800.3
        fill_in 'Date of purchase', with: "2017-05-03"
        select("My place", :from => 'product_home_id')
      end
    end

    it 'should be successful' do
      binding.pry
      within('form') do
        fill_in 'Model', with: "Iphone 6"
      end

      click_button('Create Product')
      binding.pry

      expect(current_path).to eql(product_path(Product.last))
      expect(page).to have_content 'Iphone 6'
    end

    # it 'should not be successful' do
    #   click_button('Create Product')

    #   expect(current_path).to eql('/products') # the post products actually!
    #   expect(page).to have_content(/Model can\'t be blank/)
    # end
  end
end

Заводы:

home.rb

FactoryBot.define do
  factory :home do
    sequence(:name) { |n| "My Home#{n}" }
    address 'c/ Viladomat n200 50 1a'
    insurer
    house_type
    user
  end
end

product.rb

FactoryBot.define do
  factory :product do
    model 'macbook pro'
    form_brand 'apple'
    form_product_type 'laptop'
    price_of_purchase 1200
    date_of_purchase Date.new(2017,06,06)
  end
end

user.rb

FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "myemail#{n}@mail.com" }
    password 123456
  end
end

house_type.rb

FactoryBot.define do
  factory :house_type do
    name 'Flat'
  end
end

Если я использую let!Оператор, чтобы создать дом для всех тестов, тест не проходит:

let!(:home) { create(:home, name: "My Place", user: user) }

Журнал консоли:

Capybara::ElementNotFound:
       Unable to find visible option "My place" within #<Capybara::Node::Element tag="select" path="/html/body/div[2]/form/div[4]/div/div[2]/select">

Но, если я создаю дом вручную, перед каждым тестом, он работает

let(:home) { create(:home, name: "My Place", user: user) }

before(:each) do
  home.name = "My place"
  home.save
end

Почему это пусть!не работает?Если я добавлю в свой тест binding.pry, в обоих случаях у меня будет созданный дом в моей базе данных.

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

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

FactoryBot.define do
  factory :home do
    sequence(:name) { |n| "Home#{n}" }
    address { 'c/ Viladomat n200 50 1a' } # You might want to define this to use a sequence too so it's unique when you create multiples
    insurer
    house_type
    user
  end
end

Нечто подобное позволит вам создать действительный экземпляр Home, просто вызвав create(:home).Если вы хотите настроить какие-либо из ассоциаций / параметров, вы можете передать их фабричному методу создания / сборки.Таким образом, в вашем примере это просто станет

let(:home) { create(:home, name: 'My place') }

Если вы захотите также вручную создать объект user, то вы можете вызывать login(user...) вместо того, чтобы обращаться к автоматически сгенерированному пользователю, например login(home.user...)тогда вы бы сделали

let(:user) { create(:user) }
let!(:home) { create(:home, name: 'My place', user: user }

Обратите внимание на использование let! для home вместо let.Это потому, что let лениво оценивается, поэтому экземпляр фактически не будет создан, пока вы не вызовете home в своем тесте в первый раз - поскольку при вызове login_as(user... вы не вызываете home в своем тесте, который вам нужениспользовать let! вместо этого, чтобы объект был создан до запуска теста.Возможно, вы также захотите использовать последовательности FactoryBot в таких вещах, как email вашей пользовательской фабрики, чтобы вы могли создать более одного пользователя в тестах.

Кроме того, вы вызываете expect(current_path).to eql('/new_from_camera'), чтопривести к нестабильным тестам, так как eql matcher не имеет встроенного поведения ожидания.Вместо этого вы всегда должны отдавать предпочтение предоставленным Капибарой матчерам, что будет означать колл expect(page).to have_current_path('/new_form_camera').

0 голосов
/ 23 октября 2018

Я думаю, вы можете добавить ассоциации непосредственно на фабрике home:

let(:insurer) { create(:insurer) }
let(:house_type) { create(:house_type) }
let(:user) { create(:user) }
let(:home) { create(:home, name: "My place", insurer: insurer, house_type: house_type, user: user) }
...