RSpec 系统测试无法访问工厂新创建的记录

问题描述 投票:0回答:1

我正在尝试登录用户并访问该用户拥有的记录的页面。

RSpec.describe 'User matters', type: :system do
  context 'urgent matter flow' do
    let!(:matter) { create(:matter, :with_order) }

    before :each do
      login_as(matter.patient.user) # warden helper
    end

    it 'can submit matter info' do
      visit users_matter_path(matter)
    end
  end
end

控制器很容易找到

def show
  @matter = current_user.patient.matters.find(params[:id])
  @order = @matter.orders.joins(:order_items).first
end

模板错误:

<%= @matter.id # works %> 
<%= @order.id # undefined method `id' for nil:NilClass %>

工厂:

FactoryBot.define do
  factory :matter do
    overview_image { Rack::Test::UploadedFile.new('spec/fixtures/images/bandit.jpg', 'image/jpeg') }
    overview2_image { Rack::Test::UploadedFile.new('spec/fixtures/images/bandit.jpg', 'image/jpeg') }
    patient
    doctor

    trait :with_order do
      after(:create) do |matter|
        create_list(:order, 1, matter: matter)
      end
    end
  end

  # -- Order ------------------
  factory :order do
    matter

    status { :placed }
    currency_code { 'SEK' }
  end

  # -- User -------------------
  sequence :user_email do |index|
    "patient-#{Random.hex(4)}#{index}@gmail.com"
  end

  factory :user do
    email { generate(:user_email) }
    locale { 'en' }
    country_alpha2_code { 'SE' }
    mobile_number { '+46701234567' }
    password { Faker::Internet.password(min_length: 8, max_length: 20) }
    password_confirmation { "#{password}" }
    after(:create) do |user|
      user.patient ||= create(:patient, user: user)
    end
  end

  # -- Patient -----------------
  sequence :patient_id_number do |index|
    year = index.to_s.rjust(2, '0')
    "19#{year}01019876"
  end

  factory :patient do
    id_number { generate(:patient_id_number) }
    date_of_birth { '1990-01-01' }
    first_name { 'John' }
    last_name { 'Doe' }

    user
  end
end

rails_helper.rb(省略了一些不必要的东西)

require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'

require 'rspec/rails'

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

RSpec.configure do |config|
  config.include Warden::Test::Helpers

  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, type: :system) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.append_after(:each) do
    DatabaseCleaner.clean
  end

  config.infer_spec_type_from_file_location!
  config.filter_rails_from_backtrace!
end

和spec_helper.rb(省略了一些不必要的东西)

RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
    expectations.syntax = :expect
  end

  config.before(:each, type: :system) do
    driven_by :selenium
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end
  config.shared_context_metadata_behavior = :apply_to_host_groups
  config.filter_run_when_matching :focus
  config.profile_examples = 10
  config.order = :random
end

为什么会出现这种情况?将

use_transactional_fixtures
设置为
false
不正是这个目的吗?

PS:我想测试整个用户流程,包含多个步骤。能够预先创建具有某些状态及其关系的记录,速度会快得多。

---------- 更新:

运行 rspec 时,我跟踪 test.log 并看到以下输出:

Matter Create (0.3ms)  INSERT INTO "matters" ("patient_id", "doctor_id", "reference", "symptoms", "tags", "status", "diagnosis", "planned_action", "orders_count", "matter_comments_count", "matter_responses_count", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING "id"  [["patient_id", "ed1c0576-78a4-4383-ac33-291ca44083ca"], ["doctor_id", "cdab5e14-3641-4a78-8de6-1a0056635e73"], ["reference", "pohmb"], ["symptoms", nil], ["tags", nil], ["status", "created"], ["diagnosis", nil], ["planned_action", nil], ["orders_count", 0], ["matter_comments_count", 0], ["matter_responses_count", 0], ["created_at", "2023-11-18 09:08:57.796995"], ["updated_at", "2023-11-18 09:08:57.796995"]]


  Order Create (0.3ms)  INSERT INTO "orders" ("matter_id", "reference", "currency_code", "status", "order_items_count", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["matter_id", "8533289a-4087-4664-9fe3-df4bc7a9e241"], ["reference", "evt0o"], ["currency_code", "SEK"], ["status", 0], ["order_items_count", 0], ["created_at", "2023-11-18 09:08:57.841842"], ["updated_at", "2023-11-18 09:08:57.841842"]]

因此记录已正确创建。我还验证了

matter_id
8533289a-4087-4664-9fe3-df4bc7a9e241
)是相同的。

所以我认为这可能与 rspec/系统测试和事务未提交有关?

ruby-on-rails rspec capybara
1个回答
0
投票

查看您的工厂和日志显示您没有创建任何

OrderItem
,这意味着当您尝试将其与订单连接时,不会返回任何内容:

@matter.orders.joins(:order_items).first

另外,我认为您将

joins
includes
混淆了,因为我不明白为什么您需要加入而不仅仅是:

@matter.orders.first

# which should give the same result as
@matter.orders.left_joins(:order_items).first

举例说明:

>> Order.all
=> [#<Order:0x00007f6795badfc0 id: 1>, #<Order:0x00007f6795bade80 id: 2>]

>> Order.joins(:order_items)
=> [#<Order:0x00007f67985cb690 id: 1>, #<Order:0x00007f67985cb410 id: 1>]
# NOTE: the dup orders             ^                                  ^
#       first Order has two OrderItems
#       second Order has no OrderItems so it is not in the result

>> Order.left_joins(:order_items)
=> [#<Order:0x00007f6795af6a50 id: 1>, #<Order:0x00007f6795af6910 id: 1>, #<Order:0x00007f6795af67d0 id: 2>]

first
是一样的:

>> Order.first
=> #<Order:0x00007f6798891410 id: 1>

>> Order.joins(:order_items).first
=> #<Order:0x00007f6798891410 id: 1>

>> Order.left_joins(:order_items).first
=> #<Order:0x00007f6798891410 id: 1>

如果您想预加载

order_items
,请使用
includes

>> Order.includes(:order_items)
=> [#<Order:0x00007f6795d9c598 id: 1>, #<Order:0x00007f6795d9c458 id: 2>]
# NOTE: no duplicates, all orders are loaded

>> Order.includes(:order_items).to_a.first.instance_variable_get("@association_cache")
=> 
{:order_items=>
  #<ActiveRecord::Associations::HasManyAssociation:0x00007f679799d710
...
   @stale_state=nil,
   @target=[#<OrderItem:0x00007f6795acd5d8 id: 1, order_id: 1>, #<OrderItem:0x00007f6795acd498 id: 2, order_id: 1>]>}
# NOTE: ^ order_items are preloaded into each Order object to avoid n+1 issues
© www.soinside.com 2019 - 2024. All rights reserved.