我正在尝试登录用户并访问该用户拥有的记录的页面。
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/系统测试和事务未提交有关?
查看您的工厂和日志显示您没有创建任何
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