在功能测试(水豚)中在哪里创建父模型实例?

时间:2018-10-23 14:04:22

标签: ruby-on-rails rspec capybara

我正在用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 :(得分:1)

您应该将工厂配置为自动创建所需的默认关联,以便可以在测试中创建所需的实例,而不必创建所有其他非专用记录。您的家庭工厂应该看起来像

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)实例。如果要自定义任何关联/参数,可以将它们传递给工厂的create / build方法。所以在您的示例中,它将变成

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...时不会调用{{ 1}}在测试中,您需要改用home,以便在运行测试之前创建对象。您可能还希望在用户工厂的let!之类的东西中使用FactoryBot序列,以便可以在测试中创建多个用户。

此外,您正在调用email,由于expect(current_path).to eql('/new_from_camera')匹配器没有内置的等待行为,这将导致不稳定的测试。相反,您应该始终喜欢Capybara提供的匹配器,这意味着应调用eql

答案 1 :(得分:0)

我认为您可以直接在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) }