Rspec 如何创建一个方法 "DRY" 只有请求的一些参数?
Rspec how to create an method to "DRY" only some params of a request?
我想测试我的项目的创建方法,但是这个创建方法在我的表单中有 3 个步骤,我想测试所有这些步骤。要测试每个步骤,我需要发送一个创建请求及其各自的步骤参数。
问题是:我在每一步都重复了很多参数,我想知道如何将公共参数放在一个方法中然后调用它。
这是我的 rspec 文件
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
previous_programming_experience: "false" },
step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false" },
step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false", experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"] },
step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
it 'should create a Mentee Application in api format (step 3)' do
applications = MenteeApplication.count
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
programming_language: "ruby",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false", experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"] },
step: "3", steps: "3"
expect(response).to have_http_status(200)
expect(MenteeApplication.count).to be(applications+1)
expect(flash[:notice]).to eq("Thank you for your application!")
end
end
end
如您所见,第 1 步中的参数在第 2 步和第 3 步中使用,所以我的想法是这样的:
def some_params
params.require(:application).permit(first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
previous_programming_experience: "false")
end
但是没用,我该怎么做?
let
块允许您定义在测试用例 (it
s) 中使用的变量。需要注意的一些关键点:
- 它们被延迟求值:块中的代码不会 运行 直到你调用变量(除非你使用 bang --
let!
-- 这会强制求值)
- 它们可能会在内部
context
s 中被覆盖
前往 RSpec docs 了解更多关于他们的信息。
您提供的代码可以像这样使用 let
s:
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:first_step_params) do
{
first_name: 'Mentee',
last_name: 'Rspec',
#...
previous_programming_experience: false,
}
end
let(:second_step_params) do
{
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
}.merge(first_step_params)
end
let(:third_step_params) do
{
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"],
}.merge(third_step_params)
end
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: first_step_params, step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: second_step_params, step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: third_step_params, step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
end
end
其他建议
1。不实现控制器规范
控制器是用户界面和后台服务之间的薄软件层。他们的测试很难被认为是集成(end-to-end)或单元测试。
我建议您改用功能规范。 (capybara 非常适合 Rails 测试 RSpec)
此 blog post 可能会对此提供更多见解。
2。不要在你的测试用例描述中使用 should
参见 betterspecs.org。
3。注意
中的最后一个尾随逗号
let(:application_params) do
{
first_name: 'Mentee',
last_name: 'Rspec',
#...
previous_programming_experience: false,
}
end
它可以防止 incidental changes。
4。使用 .rspec 文件
内容如
--require rails_helper
因此您不需要在每个规范文件顶部添加 require 'rails_helper'
。
5。使用 context
s
这也是 betterspecs.org 的指导。你可以做类似
的事情
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:application_params) do
{
#...
}
end
let(:step) { 1 }
it 'should start create a Mentee Application' do
edition
post :create, application: application_params, step: step, steps: "3"
expect(response).to have_http_status(200)
end
context 'in second step' do
let(:step) { 2 }
it 'should continue to create a Mentee Application' do
post :create, application: application_params, step: step, steps: "3"
expect(response).to have_http_status(200)
end
end
end
end
context
s 也可以方便地处理额外的参数:
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:application_params) do
common_params.merge(additional_params)
end
let(:commom_params) do
{
#...
}
end
let(:additional_params) { {} }
it 'creates an application' do
post :create, application: application_params
end
context 'with API params' do
let(:additional_params) do
{
#...
}
end
it 'creates an application' do
post :create, application: application_params
end
end
end
end
请注意,post
方法调用在两种上下文中变得完全相同。这将允许重复使用它(在 before
块甚至另一个 let
块中)。
我想我会很想像下面那样做。本质上:
创建一个名为 @full_application
的 memoized 变量并将其包装在一个方法中(我在测试的底部完成了此操作)。
创建常量来规定每个测试所需的值的子集,例如 STEP_ONE_PARAMS
、STEP_TWO_PARAMS
等
在每个 it
块中,使用 .slice
和上面定义的常量来 "grab" 您要使用的 full_application
中的值。
像这样:
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
STEP_ONE_PARAMS = %w(
first_name
last_name
email
gender
country
communicating_in_english
send_to_mentor_confirmed
time_availability
previous_programming_experience
).freeze
STEP_TWO_PARAMS = STEP_ONE_PARAMS.dup.concat(%w(
motivation
background
team_work_experience
)).freeze
STEP_THREE_PARAMS = STEP_TWO_PARAMS.dup.concat(%w(
operating_system
project_proposal
roadmap
engagements
)).freeze
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: full_application.slice(*STEP_ONE_PARAMS),
step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: full_application.slice(*STEP_TWO_PARAMS),
step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: full_application.slice(*STEP_THREE_PARAMS),
step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
it 'should create a Mentee Application in api format (step 3)' do
applications = MenteeApplication.count
post :create, application: full_application,
step: "3", steps: "3"
expect(response).to have_http_status(200)
expect(MenteeApplication.count).to be(applications+1)
expect(flash[:notice]).to eq("Thank you for your application!")
end
end
end
def full_application
@full_application ||= {
first_name: "Mentee",
last_name: "Rspec",
email: "mentee@email.com",
gender: "female",
country: "IN",
program_country: "IN",
time_zone: "5 - Mumbai",
communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
programming_language: "ruby",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false",
experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: [
"master_student",
"part_time",
"volunteer",
"one_project"
]
}
end
我想测试我的项目的创建方法,但是这个创建方法在我的表单中有 3 个步骤,我想测试所有这些步骤。要测试每个步骤,我需要发送一个创建请求及其各自的步骤参数。
问题是:我在每一步都重复了很多参数,我想知道如何将公共参数放在一个方法中然后调用它。
这是我的 rspec 文件
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
previous_programming_experience: "false" },
step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false" },
step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false", experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"] },
step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
it 'should create a Mentee Application in api format (step 3)' do
applications = MenteeApplication.count
post :create, application: {
first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
programming_language: "ruby",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false", experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"] },
step: "3", steps: "3"
expect(response).to have_http_status(200)
expect(MenteeApplication.count).to be(applications+1)
expect(flash[:notice]).to eq("Thank you for your application!")
end
end
end
如您所见,第 1 步中的参数在第 2 步和第 3 步中使用,所以我的想法是这样的:
def some_params
params.require(:application).permit(first_name: "Mentee", last_name: "Rspec", email: "mentee@email.com",
gender: "female", country: "IN", program_country: "IN",
time_zone: "5 - Mumbai", communicating_in_english: "true",
send_to_mentor_confirmed: "true",
time_availability: 3,
previous_programming_experience: "false")
end
但是没用,我该怎么做?
let
块允许您定义在测试用例 (it
s) 中使用的变量。需要注意的一些关键点:
- 它们被延迟求值:块中的代码不会 运行 直到你调用变量(除非你使用 bang --
let!
-- 这会强制求值) - 它们可能会在内部
context
s 中被覆盖
前往 RSpec docs 了解更多关于他们的信息。
您提供的代码可以像这样使用 let
s:
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:first_step_params) do
{
first_name: 'Mentee',
last_name: 'Rspec',
#...
previous_programming_experience: false,
}
end
let(:second_step_params) do
{
motivation: "Motivation",
background: "Background",
team_work_experience: "Team Work Experience",
}.merge(first_step_params)
end
let(:third_step_params) do
{
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: ["master_student", "part_time", "volunteer", "one_project"],
}.merge(third_step_params)
end
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: first_step_params, step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: second_step_params, step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: third_step_params, step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
end
end
其他建议
1。不实现控制器规范
控制器是用户界面和后台服务之间的薄软件层。他们的测试很难被认为是集成(end-to-end)或单元测试。
我建议您改用功能规范。 (capybara 非常适合 Rails 测试 RSpec)
此 blog post 可能会对此提供更多见解。
2。不要在你的测试用例描述中使用 should
参见 betterspecs.org。
3。注意
中的最后一个尾随逗号let(:application_params) do
{
first_name: 'Mentee',
last_name: 'Rspec',
#...
previous_programming_experience: false,
}
end
它可以防止 incidental changes。
4。使用 .rspec 文件
内容如
--require rails_helper
因此您不需要在每个规范文件顶部添加 require 'rails_helper'
。
5。使用 context
s
这也是 betterspecs.org 的指导。你可以做类似
的事情RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:application_params) do
{
#...
}
end
let(:step) { 1 }
it 'should start create a Mentee Application' do
edition
post :create, application: application_params, step: step, steps: "3"
expect(response).to have_http_status(200)
end
context 'in second step' do
let(:step) { 2 }
it 'should continue to create a Mentee Application' do
post :create, application: application_params, step: step, steps: "3"
expect(response).to have_http_status(200)
end
end
end
end
context
s 也可以方便地处理额外的参数:
RSpec.describe Api::MenteeApplicationsController, type: :controller do
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
let(:application_params) do
common_params.merge(additional_params)
end
let(:commom_params) do
{
#...
}
end
let(:additional_params) { {} }
it 'creates an application' do
post :create, application: application_params
end
context 'with API params' do
let(:additional_params) do
{
#...
}
end
it 'creates an application' do
post :create, application: application_params
end
end
end
end
请注意,post
方法调用在两种上下文中变得完全相同。这将允许重复使用它(在 before
块甚至另一个 let
块中)。
我想我会很想像下面那样做。本质上:
创建一个名为
@full_application
的 memoized 变量并将其包装在一个方法中(我在测试的底部完成了此操作)。创建常量来规定每个测试所需的值的子集,例如
STEP_ONE_PARAMS
、STEP_TWO_PARAMS
等在每个
it
块中,使用.slice
和上面定义的常量来 "grab" 您要使用的full_application
中的值。
像这样:
require 'rails_helper'
RSpec.describe Api::MenteeApplicationsController, type: :controller do
STEP_ONE_PARAMS = %w(
first_name
last_name
email
gender
country
communicating_in_english
send_to_mentor_confirmed
time_availability
previous_programming_experience
).freeze
STEP_TWO_PARAMS = STEP_ONE_PARAMS.dup.concat(%w(
motivation
background
team_work_experience
)).freeze
STEP_THREE_PARAMS = STEP_TWO_PARAMS.dup.concat(%w(
operating_system
project_proposal
roadmap
engagements
)).freeze
describe "Api Mentee Application controller tests" do
let(:edition) { create(:edition) }
it 'should start create a Mentee Application, step 1' do
edition
post :create, application: full_application.slice(*STEP_ONE_PARAMS),
step: "1", steps: "3"
expect(response).to have_http_status(200)
end
it 'should continue to create a Mentee Application, step 2' do
post :create, application: full_application.slice(*STEP_TWO_PARAMS),
step: "2", steps: "3"
expect(response).to have_http_status(200)
end
it 'should not create a Mentee Application in api format' do
applications = MenteeApplication.count
post :create, application: full_application.slice(*STEP_THREE_PARAMS),
step: "3", steps: "3"
expect(response).to have_http_status(:unprocessable_entity)
expect(MenteeApplication.count).to be(0)
end
it 'should create a Mentee Application in api format (step 3)' do
applications = MenteeApplication.count
post :create, application: full_application,
step: "3", steps: "3"
expect(response).to have_http_status(200)
expect(MenteeApplication.count).to be(applications+1)
expect(flash[:notice]).to eq("Thank you for your application!")
end
end
end
def full_application
@full_application ||= {
first_name: "Mentee",
last_name: "Rspec",
email: "mentee@email.com",
gender: "female",
country: "IN",
program_country: "IN",
time_zone: "5 - Mumbai",
communicating_in_english: "true",
send_to_mentor_confirmed: "true",
motivation: "Motivation",
background: "Background",
programming_language: "ruby",
team_work_experience: "Team Work Experience",
previous_programming_experience: "false",
experience: "",
operating_system: "mac_os",
project_proposal: "Project Proposal",
roadmap: "Roadmap",
time_availability: 3,
engagements: [
"master_student",
"part_time",
"volunteer",
"one_project"
]
}
end