I have simple action show
def show
#field = Field.find_by(params[:id])
end
and i want write spec for it
require 'spec_helper'
RSpec.describe FieldsController, type: :controller do
let(:field) { create(:field) }
it 'should show field' do
get :show, id: field
expect(response.status).to eq(200)
end
end
but I have got an error
Failure/Error: get :show, id: field
ArgumentError:
unknown keyword: id
How to fix it?
HTTP request methods will accept only the following keyword arguments
params, headers, env, xhr, format
According to the new API, you should use keyword arguments, params in this case:
it 'should show field' do
get :show, params: { id: field.id }
expect(response.status).to eq(200)
end
Related
I am testing my controller with RSPEC using shoulda matchers while i came across the create method in my controller i cant test the save function if i try to do that i go the error
Expected response to be a <3XX: redirect>, but was a <200: OK>
i have attached my controller part and testing and route
In testing
RSpec.describe "routes for home", type: :routing do
describe 'post #create' do
before do
post :create , params: params
end
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
expect(assigns[:restaurant].save).to redirect_to(gridurl_path)
end
end
end
end
In controller
def create
# render plain: params
#restaurant=Restaurant.new(restaurant_params)
if #restaurant.save
redirect_to gridurl_path
else
render 'index'
end
end
In routes
post "/home/create", to: "home#create", as: :createurl
get '/home/grid', to: 'home#grid',as: :gridurl
Thank you in advance
First I suggest you read https://relishapp.com/rspec/rspec-rails/docs/controller-specs and also the other docs. They will give you a good starting point on how to test stuff with rspec.
When you look at a controller action, you are not interested on who's doing what (i.e assigns[:restaurant]) - you want to see if a redirect happens, if something is saved in the DB, etc. Think of it from the perspective of a user calling that endpoint. Does the user know all of the internals?
Here is how it should look like:
describe "routes for home", type: :controller do
describe 'post #create' do
context 'when the params are correct' do
let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } }
it 'is expected save successfully and redirect_to gridpage' do
post :create, params: params
expect(response).to redirect_to('/home/grid')
end
end
end
end
I have two problems when I try to test the update action with RSpec, here is the controller file:
#volunteers_controller.rb
module Api
module V1
class VolunteersController < ApplicationController
before_action :find_volunteer, only: %i[show update destroy]
def update
#volunteer.update!(volunteer_params)
head :no_content
end
private
def find_volunteer
#volunteer = Volunteer.find_by!(id: params[:id])
end
def volunteer_params
params.require(:volunteer).permit(:image_url, :name, :job_desc)
end
end
end
end
Here is the test file:
require 'rails_helper'
RSpec.describe Api::V1::VolunteersController, type: :request do
...
describe '#update' do
let(:volunteer) { Volunteer.create!( :image_url=>"first.jpg", :name=>"test1", :job_desc=>"description") }
let(:params){
{:volunteer => {
"image_url"=>"new.jpg",
"name"=>"test1",
"job_desc"=>"description"
}
}
}
it 'updates a certain volunteer' do
patch :patch, :params => params #failed, bad URL
expect(volunteer.image_url).to eq("new.jpg") #failed, still return 'first.jpg'
end
it 'returns a no_content header' do
patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params
expect(response).to have_http_status "204"
end
end
end
private
def json_parse(string)
if string.class==String
json = JSON.parse(string)
end
json
end
So my questions are:
when try to write the URL like this: patch :patch, :params => params, I got the following error:
Api::V1::VolunteersController#update updates a certain volunteer
Failure/Error: patch :patch, :params => params
URI::InvalidURIError:
bad URI(is not URI?): "http://www.example.com:80patch"
How can I change the URL to: "http://localhost:3000/api/v1/volunteers/#{volunteer.id}"?
I manually test the update action, putting a binding.pry in the update action, it does update volunteer subject, however, when it goes back to the test, it shows that it doesn't not get updated, why is that?
Thank you!!
The first problem is really your update method itself and its complete lack of error handling and meaningful feedback to the client. update! will raise ActiveRecord::RecordInvalid if the input is invalid - which is not rescued at all in your controller. And exceptions should no be used for normal code flow - invalid input is not really an exceptional event.
Instead you should rewrite your controller so that it checks if the update is performed and returns the appropriate response:
def update
if #volunteer.update(volunteer_params)
head :no_content
else
head :unprocessable_entity
end
end
As for the spec itself you're mixing up controller specs and request specs. While they look somewhat similar the key difference is that a request spec sends actual HTTP requests your rails server while a controller spec stubs the actual request and passes it to an instance of the controller under test.
In a controller spec you could write:
patch :update, params: { ... }
Because its actually calling the update method on an instance of the controller. But of course:
patch :patch, :params => params #failed, bad URL
Will not work in request spec since its not a valid URL and request specs send actual HTTP requests. Note that you should pass relative URLs and not absolute URLs as the test server may run on a different port then the dev server
# Bad
patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params
# Good
patch "/api/v1/volunteers/#{volunteer.id}", params: params
ActiveRecord models are not "live reloading" - the representation in memory will not automatically be updated when the values in the database are updated. You need to manaully reload the record for that to happen:
it 'updates a certain volunteer' do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
volunteer.reload
expect(volunteer.image_url).to eq("new.jpg")
end
Altogether your spec should actually look something like:
# Describe the endpoint - not the controller implmentation
RSpec.describe "V1 Volunteers API", type: :request do
describe 'PATCH /api/v1/volunteers/:id' do
# use do ... end if the expression does not fit on one line
let(:volunteer) do
# enough with the hashrockets already!
Volunteer.create!(
image_url: "first.jpg",
name: "test1",
job_desc: "description"
)
end
context "with invalid parameters" do
# some set of failing parameters
let(:params) do
{
volunteer: {
name: ""
}
}
end
it "returns unproccessable entity" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect(resonse).to have_http_status :unproccessable_entity
end
it "does not update the volunteer" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect { volunteer.reload }.to_not change(volunteer, :name).to("")
end
end
context "with valid parameters" do
# some set of failing parameters
let(:params) do
{
volunteer: {
image_url: "new.jpg",
name: "test1",
job_desc: "description"
}
}
end
it "returns no content" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect(resonse).to have_http_status :no_content
end
it "updates the volunteer" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect { volunteer.reload }.to change(volunteer, :image_url)
.to("new.jpg")
end
end
end
end
I have a basic Rails API built with Accounts and Users. All of the account specs pass when I remove...
before_action :authenticate_user!
But with that in place, I'm having trouble getting the specs to pass.
# Note `json` is a custom helper to parse JSON responses
RSpec.describe 'Account API', type: :request do
# test data
let!(:user) { create(:user) }
let!(:accounts) { create_list(:account, 10, user_id: user.id) }
let(:account_id) { accounts.first.id }
# GET /accounts
describe 'GET /accounts' do
# HTTP request before examples
before do
get '/accounts'
request.headers.merge! user.create_new_auth_token
end
it 'returns accounts' do
expect(json).not_to be_empty
expect(json.size).to eq(10)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
end
As you can see I attempted to merge the auth token with this line in a before do block...
request.headers.merge! user.create_new_auth_token
But that is not working. Instead I get json.size == 1 and http_status of 401 unauthorized.
I was able to fix it by declaring a variable at the top...
let(:auth_headers) { create(:user).create_new_auth_token }
And in my specs using it like this...
before { get '/accounts', headers: auth_headers }
When I run my spec I get the following error:
1) TasksController#index returns incompleted tasks
Failure/Error: it { expect(json).to_have(1).tasks }
NameError:
undefined local variable or method `json' for #<RSpec::ExampleGroups::TasksController::Index::ReturnsIncompletedTasks:0x00000005df8018>
Here's the spec:
describe "#index" do
let!(:task){ FactoryGirl.create(:task) }
context 'returns incompleted tasks' do
before do
get 'index', user_id: user.id, format: :json
end
it { expect(json).to_have(1).tasks }
end
end
Here's my controller:
def index
#Some stuff
render json: tasks
end
Why is this so?
You are doing it wrong. This is how you test for json response
expect(response).to have_http_status(:success)
expect {
JSON.parse(response.body)
}.to_not raise_error
To use the actual content you first need to parse it
result = JSON(response.body)
expect(result.length).to eq(1)
I have a following method in my controller:
def create
#job = Job.new(job_params)
if #job.save
render 'create_success', status: :created
else
render 'create_failure', status: :bad_request
end
end
And here are my controller specs for this:
require 'rails_helper'
RSpec.describe Api::V1::JobsController, :type => :controller do
let(:actor) { FactoryGirl.create(:user, :service_advisor) }
let(:auth_token) { actor.authentication_token }
describe 'POST #create' do
render_views
context 'with invalid attrubutes' do
let(:job) { FactoryGirl.build(:job, customer_id: nil).attributes }
it 'renders create_failure view' do
expect(response).to render_template :create_failure
end
it 'returns 400 status code' do
expect(response.status).to eq 400
end
end
end
end
However, for some reason, no matter what i do, the controller spec allways thinks that this method returns empty response and status 200. I can test that method using postman or curl and it works as expected - return error messages and status codes (201/400) but my spec allways sees it as empty response with 200 status code.
It seems that render_views is not working. My env is:
ruby 2.0.0p594
Rails 4.1.0
rspec 3.1.5
You have not given call to create action in your test case.
it 'renders create_failure view' do
post :create, job
expect(response).to render_template :create_failure
end