如何使用 Rspec 在 FactoryBot 生成的模型上测试 "Model.valid?"?
How to test "Model.valid?" on a FactoryBot generated model using Rspec?
我创建了一个聊天室模型,它首先验证某些字段的存在,因此在控制器中我创建了聊天室,然后使用 .valid?
方法确定响应来检查它是否有效。现在,当我使用 FactoryBot 创建测试模型时,测试不会通过 if
语句,它会 returns 响应,就好像测试已经完成一样。
我的操作代码
def create
new_chatroom = Chatroom.create(chatroom_params)
if new_chatroom.valid?
new_chatroom.members.create({ user_id: @current_user[:username] })
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
工厂代码
FactoryBot.define do
factory :chatroom do
topic { Faker::Lorem.unique.question }
slug { Faker::IndustrySegments.unique.sub_sector }
description { Faker::Lorem.paragraph }
owner { Faker::Name.first_name }
public { true }
end
end
这是我的测试
it "creates a new chatroom" do
post :create, params: {
:topic => "test chatroom",
:slug => "code-testing",
:description => "Testing with Rspec",
}
expect(response).to have_http_status(:created)
end
这里是 render_response
方法:
def render_response(resource, status)
if block_given?
yield(resource, status)
else
render json: resource, :status => status
end
end
测试失败:
Failure/Error: expect(response).to have_http_status(:created)
expected the response to have status code :created (201) but it was :ok (200)
我遇到了这个失败,当我试图让它通过时(误报),覆盖范围显示我正在测试的响应并不是我实际操作的响应,因为其余行从 if
语句不被覆盖。
but I thought FactoryBot takes over the whole model creation in the
tests.
否 - FactoryBot 仅提供创建模型实例的工厂。这被广泛用作在测试前填充数据库的固定装置的替代品。与灯具不同,这不是自动的。
除了生成器将创建工厂文件这一事实之外,仅添加 FactoryBot 对您的应用程序绝对没有任何改变。它不会以任何方式影响模型的行为。
在测试资源创建时,您需要测试:
- 给定有效参数,然后模型应该持久保存到数据库
- 给定有效参数,则响应应该成功并指向新创建的资源。
- 给定无效参数,则不应将模型持久保存到数据库
- 给定无效参数,则响应应为 422 并呈现错误页面。
您想使用请求规范而不是控制器规范来测试它。
Request specs provide a high-level alternative to controller specs. In
fact, as of RSpec 3.5, both the Rails and RSpec teams discourage
directly testing controllers in favor of functional tests like request
specs.
require "rails_helper"
RSpec.describe "Chatroom creation", type: :request do
let(:valid_params) do
{
chatroom: {
topic: "test chatroom",
slug: "code-testing",
description: "Testing with Rspec"
}
}
end
let(:invalid_params) do
{
chatroom: {
topic: ''
}
}
end
context "when the parameters are valid" do
it "creates a new chatroom" do
expect do
post '/chatrooms', params: valid_params
end.to change(Chatroom, :count).by(1)
end
it "returns success" do
post '/chatrooms', params: valid_params
expect(response).to have_http_status(:created)
end
end
context "when the parameters are invalid" do
it "does not create a new chatroom" do
expect do
post '/chatrooms', params: invalid_params
end.to_not change(Chatroom, :count)
end
it "returns bad entity" do
post '/chatrooms', params: invalid_params
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
然后我们可以解决您的控制器的问题,它应该是:
class ChatroomsController < ApplicationController
# ...
def create
new_chatroom = Chatroom.new(chatroom_params)
if new_chatroom.save
new_chatroom.members.create(user_id: @current_user[:username])
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
end
您永远不应使用 .valid?
检查记录是否已保存到数据库中。它仅确保模型级别验证已通过。并不是来自 .create
的 INSERT 语句实际上在数据库中创建了一行。有关可能发生的情况的示例,请参阅 The Perils of Uniqueness Validations。
虽然您可以使用 new_chatroom.persisted?
,但这是常见的 rails 惯用语,因为它为您提供了一个变量,您可以在记录持久化之前对其进行操作。
class ChatroomsController < ApplicationController
# ...
def create
new_chatroom = Chatroom.new(chatroom_params)
new_chatroom.members.new(user: current_user)
if new_chatroom.save
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
end
我不会尝试在控制器规范中测试模型有效性,而是编写请求规范(如其他受访者所建议的)。您可以像这样在模型本身上测试对象的有效性:
context 'valid' do
let(:chatroom) { create :chatroom }
it { expect(chatroom).to be_valid }
end
context 'fails validation' do
let(:chatroom) { create :chatroom, topic: nil }
it { expect(chatroom).not_to be_valid }
end
资源:https://www.rubydoc.info/gems/rspec-rails/RSpec%2FRails%2FMatchers:be_valid
但是如果你想检查实际字段是否经过验证,我建议在模型上使用 shoulda 匹配器,如下所示:
it { should validate_presence_of :topic }
it { should validate_presence_of :slug }
我创建了一个聊天室模型,它首先验证某些字段的存在,因此在控制器中我创建了聊天室,然后使用 .valid?
方法确定响应来检查它是否有效。现在,当我使用 FactoryBot 创建测试模型时,测试不会通过 if
语句,它会 returns 响应,就好像测试已经完成一样。
我的操作代码
def create
new_chatroom = Chatroom.create(chatroom_params)
if new_chatroom.valid?
new_chatroom.members.create({ user_id: @current_user[:username] })
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
工厂代码
FactoryBot.define do
factory :chatroom do
topic { Faker::Lorem.unique.question }
slug { Faker::IndustrySegments.unique.sub_sector }
description { Faker::Lorem.paragraph }
owner { Faker::Name.first_name }
public { true }
end
end
这是我的测试
it "creates a new chatroom" do
post :create, params: {
:topic => "test chatroom",
:slug => "code-testing",
:description => "Testing with Rspec",
}
expect(response).to have_http_status(:created)
end
这里是 render_response
方法:
def render_response(resource, status)
if block_given?
yield(resource, status)
else
render json: resource, :status => status
end
end
测试失败:
Failure/Error: expect(response).to have_http_status(:created)
expected the response to have status code :created (201) but it was :ok (200)
我遇到了这个失败,当我试图让它通过时(误报),覆盖范围显示我正在测试的响应并不是我实际操作的响应,因为其余行从 if
语句不被覆盖。
but I thought FactoryBot takes over the whole model creation in the tests.
否 - FactoryBot 仅提供创建模型实例的工厂。这被广泛用作在测试前填充数据库的固定装置的替代品。与灯具不同,这不是自动的。
除了生成器将创建工厂文件这一事实之外,仅添加 FactoryBot 对您的应用程序绝对没有任何改变。它不会以任何方式影响模型的行为。
在测试资源创建时,您需要测试:
- 给定有效参数,然后模型应该持久保存到数据库
- 给定有效参数,则响应应该成功并指向新创建的资源。
- 给定无效参数,则不应将模型持久保存到数据库
- 给定无效参数,则响应应为 422 并呈现错误页面。
您想使用请求规范而不是控制器规范来测试它。
Request specs provide a high-level alternative to controller specs. In fact, as of RSpec 3.5, both the Rails and RSpec teams discourage directly testing controllers in favor of functional tests like request specs.
require "rails_helper"
RSpec.describe "Chatroom creation", type: :request do
let(:valid_params) do
{
chatroom: {
topic: "test chatroom",
slug: "code-testing",
description: "Testing with Rspec"
}
}
end
let(:invalid_params) do
{
chatroom: {
topic: ''
}
}
end
context "when the parameters are valid" do
it "creates a new chatroom" do
expect do
post '/chatrooms', params: valid_params
end.to change(Chatroom, :count).by(1)
end
it "returns success" do
post '/chatrooms', params: valid_params
expect(response).to have_http_status(:created)
end
end
context "when the parameters are invalid" do
it "does not create a new chatroom" do
expect do
post '/chatrooms', params: invalid_params
end.to_not change(Chatroom, :count)
end
it "returns bad entity" do
post '/chatrooms', params: invalid_params
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
然后我们可以解决您的控制器的问题,它应该是:
class ChatroomsController < ApplicationController
# ...
def create
new_chatroom = Chatroom.new(chatroom_params)
if new_chatroom.save
new_chatroom.members.create(user_id: @current_user[:username])
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
end
您永远不应使用 .valid?
检查记录是否已保存到数据库中。它仅确保模型级别验证已通过。并不是来自 .create
的 INSERT 语句实际上在数据库中创建了一行。有关可能发生的情况的示例,请参阅 The Perils of Uniqueness Validations。
虽然您可以使用 new_chatroom.persisted?
,但这是常见的 rails 惯用语,因为它为您提供了一个变量,您可以在记录持久化之前对其进行操作。
class ChatroomsController < ApplicationController
# ...
def create
new_chatroom = Chatroom.new(chatroom_params)
new_chatroom.members.new(user: current_user)
if new_chatroom.save
render_response(new_chatroom, :created)
else
render_error_response(new_chatroom.errors, :bad_request)
end
end
end
我不会尝试在控制器规范中测试模型有效性,而是编写请求规范(如其他受访者所建议的)。您可以像这样在模型本身上测试对象的有效性:
context 'valid' do
let(:chatroom) { create :chatroom }
it { expect(chatroom).to be_valid }
end
context 'fails validation' do
let(:chatroom) { create :chatroom, topic: nil }
it { expect(chatroom).not_to be_valid }
end
资源:https://www.rubydoc.info/gems/rspec-rails/RSpec%2FRails%2FMatchers:be_valid
但是如果你想检查实际字段是否经过验证,我建议在模型上使用 shoulda 匹配器,如下所示:
it { should validate_presence_of :topic }
it { should validate_presence_of :slug }