我正在使用 rspec 3.12 重构基于 Rails 5.2.4 的应用程序。我专注于尚不存在的请求测试。
在为 Playgrounds 控制器定义 requests 测试时,我希望重新使用有助于验证 Playground 模型的工厂。我在途中遇到了几个错误,现在我在以前使用的登录功能上遇到了困难。以下是第一个请求:
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个问题:
谢谢您的帮助!
你做的集成测试是完全错误的。你真正想要的更像是:
RSpec.describe "Playgrounds", type: :request do
# This should be handled with a trait
let(:user) { FactoryBot.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 "GET /index" do
let(:playgrounds) { FactoryBot.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 /show" 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 /create" do
context "with valid attributes" do
# Use per context let instead
let(:attributes) { FactoryBot.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 "creates a playground" do
expect {
post '/playgrounds', params: {
playground: attributes
}
}.to change(Playground, :count).by(1)
expect(response).to be_successful
end
end
end
end
FactoryBot.create
只能用于在示例运行之前填充数据库。您不应该使用它来尝试创建无效记录,因为这会引发错误。
如果您想在测试创建/更新操作时重用工厂定义,请使用
FactoryBot.attributes_for
获取属性哈希,或使用 FactoryBot#build
获取未保存的模型实例。
在这里重用工厂听起来是避免 DRY 的好主意。是吗?
如果您认同 DRY 方法,那么这不是您想要避免的事情。但是,是的,使用工厂可以用来避免重复,但它们也会降低规范的可读性,因为您必须查看工厂定义才能准确了解传递的内容。
如何在请求上下文中使用login_as方法?
完全取决于您使用的身份验证系统。如果您使用的是 Devise,则只需使用 Warden 提供的 测试助手 即可使身份验证系统短路。这比让每个测试发送额外的 HTTP 请求来登录用户要高效得多。
该路径应该由专门针对身份验证系统的单独测试覆盖。