Reputation: 4600
I want to test a create method of my project, but this create method has 3 steps in my form and I want to test all of them. To test each step I need to send a create request with their respective params of the step.
The problem is: I am repeating many params in each step, I want to know how can I put the common params in a method and then just call it.
Here is my rspec file
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: "[email protected]",
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: "[email protected]",
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: "[email protected]",
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: "[email protected]",
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
As you can see, the params in step 1 are used in steps 2 and 3, so I was thinking in something like this:
def some_params
params.require(:application).permit(first_name: "Mentee", last_name: "Rspec", email: "[email protected]",
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
But didn't work, how can I do that?
Upvotes: 1
Views: 1410
Reputation: 581
let
blocks allow you to define variables for using within the tests cases (it
s). Some key points to be aware of:
let!
-- which forces the evaluation)context
sHead to RSpec docs to know more about them.
The code you provided could make use of let
s just like this:
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
Controllers are meant to be a thin software layer between the user interface and background services. Their tests can hardly be acknowledged as integration (end-to-end) nor unit tests.
I'd suggest you to implement feature specs instead. (capybara is a great match for Rails testing with RSpec)
This blog post might provide more insights on this.
See betterspecs.org.
let(:application_params) do
{
first_name: 'Mentee',
last_name: 'Rspec',
#...
previous_programming_experience: false,
}
end
It prevents incidental changes.
With contents such as
--require rails_helper
So you don't need require 'rails_helper'
on top of each spec file.
context
sThis is also a guidance from betterspecs.org. You could do something like
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 might also be handy for handling additional params:
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
Note that the post
method call became exactly the same in both contexts. This would allow for reusing it (in a before
block or even another let
block).
Upvotes: 2
Reputation: 20263
I think I would be tempted to do it something like below. Essentially:
Create a memoized variable called @full_application
and wrap it in a method (I've done this at the bottom of the test).
Create constants stipulating the subsets of the values that you want for each test, such as STEP_ONE_PARAMS
, STEP_TWO_PARAMS
, etc.
In each it
block, use .slice
and the constants defined above to "grab" the values from full_application
that you want to use.
Something like this:
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: "[email protected]",
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
Upvotes: 0