提问人:user1185081 提问时间:10/31/2023 更新时间:11/1/2023 访问量:38
使用 RSPEC 进行测试时,如何重用 FactorBot 工厂来构建请求属性?
How to re-use FactorBot factories to build request attributes when testing with RSPEC?
问:
我正在重构一个基于 Rails 5.2.4 的应用程序,使用 rspec 3.12。我专注于尚不存在的请求测试。
在为 Playgrounds 控制器定义请求测试时,我希望重用有助于验证 Playground 模型的 Factories。我在途中遇到了几个错误,我现在偶然发现了以前使用的登录功能。以下是第一个请求:
RSpec.describe "/playgrounds", type: :request do
# Playground. As you add validations to Playground, be sure to
# adjust the attributes here as well.
let(:valid_pg) {FactoryBot.create(:playground)}
let(:invalid_pg) {FactoryBot.create(:playground, status_id: nil)} # Invalid as status is mandatory
let(:valid_attributes) { valid_pg.attributes.except("id") } # Exclude id as it is generated
let(:invalid_attributes) { invalid_pg.attributes.except("id") }
# Login as used in the features validation
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user, is_admin: true)
login_as test_user, scope: :user
end
describe "GET /index" do
it "1-renders a successful response" do
valid_attributes["code"].next!
puts valid_attributes
puts invalid_attributes
puts playgrounds_url
Playground.create! valid_attributes
get playgrounds_url
expect(response).to be_successful
end
end
describe "GET /show" do
it "2-renders a successful response" do
valid_attributes["code"].next!
playground = Playground.create! valid_attributes
get playground_url(playground)
expect(response).to be_successful
end
end
对于每个测试,都会引发以下错误:
Failure/Error: login_as test_user, scope: :user
NoMethodError:
undefined method `login_as' for #<RSpec::ExampleGroups::Playgrounds::GETIndex:0x000001808eda7130>
然后我有 2 个问题:
- 在这里重用工厂听起来是避免 DRY 的好主意。是吗?
- 如何在请求的上下文中使用 login_as 方法?
感谢您的帮助!
答:
你真的在很多方面都错过了这个目标。
你真正想要的更像是:
RSpec.describe "Playgrounds", type: :request do
# this should be done in your test setup
include FactoryBot::Syntax::Methods
# This should be handled with a trait
let(:user) { create(:user, is_admin: true) }
let(:playground) { FactoryBot.create(:playground) }
before do
# get "/users/sign_in" - don't think you actually need this
login_as user, scope: :user
end
# Describe the route and not the controller
describe "GET /playgrounds" do
let!(:playgrounds) { create_list(:playground, 3) }
# Don't do that wonky numbering stuff - you don't need it
# and it will just confuse you when the examples run in random order
it "renders a successful response" do
get playgrounds_url
# this is ok as a litmus test but doesn't tell you if the controller
# is actually providing a meaningful response
expect(response).to be_successful
end
# @todo write a test that it actually responds with the playgrounds
end
describe "GET /playgrounds/:id" do
it "renders a successful response" do
get playground_url(playground)
# this is ok as a litmus test but doesn't tell you if the controller
# is actually providing a meaningful response
expect(response).to be_successful
end
# @todo write a test that it actually responds with the playground
end
describe "POST /playgrounds" do
context "with valid attributes" do
# Use per context let instead
let(:attributes) { attributes_for(:playground) }
it "creates a playground" do
expect {
post '/playgrounds', params: {
playground: attributes
}
}.to change(Playground, :count).by(1)
expect(response).to be_successful
end
end
context "with invalid attributes" do
# Use per context let instead
let(:attributes) do
# I would just define this manually
{
foo: ""
}
end
it "doesn't create a playground" do
expect {
post '/playgrounds', params: {
playground: attributes
}
}.to_not change(Playground, :count)
expect(response).to have_http_status :unprocessable_entity
end
end
end
end
FactoryBot.create
并且应该用于填充数据库作为测试设置。您不应该使用它来尝试创建无效记录,因为这会引发错误并且没有意义。索引和显示功能的规范实际上应该只依赖于测试设置来创建前提条件。create_list
如果要在测试创建/更新操作时重用工厂定义,请使用 获取属性的哈希值或仅获取模型的未保存实例。FactoryBot.attributes_for
FactoryBot.build
在这里重用工厂听起来是避免 DRY 的好主意。是吗?
如果您订阅 DRY 方法,那么这不是您想要避免的事情。但是,是的,使用工厂可以避免重复,但它们也会降低规范的可读性,因为您必须查看工厂定义才能确切地看到传递的内容。
如何在请求上下文中使用 login_as 方法?
这完全取决于您使用的身份验证系统。如果您使用的是 Devise,则可以使用 Warden 提供的测试助手,这会使身份验证系统短路。这比让每个测试发送额外的 HTTP 请求来登录用户要高效得多。
实际上,登录步骤应该由专门针对身份验证系统的单独测试(功能/系统规范)来涵盖。
如果您自己拼凑了一些东西,则可以存根提供用户的方法,也可以完成直接发布到登录用户的方法的步骤。集成测试使用一种模拟浏览器中 cookie 处理的机制。
我不知道应该做什么,但为什么这是你的测试应该关注的事情?这似乎是内部悬空的,而且很臭。valid_attributes["code"].next!
评论
ModelName.new
expect(model.valid?).to be true/false