How to examine the response of an API post in an RSpec? - ruby-on-rails

Below is my rspec. Notice that three lines are commented out. This rspec passes.
describe "/my_api_endpoint" do
it 'Blah Blah Blah' do
post "/my_api_endpoint", #params
# expect(response).to be_success
# json = JSON.parse(response.body)
# puts "json = #{json}"
last_response.should be_ok
end
end
However, not only do I need to know that the POST returned success, I also need to examine the JSON it returns. Somehow I cannot do this. When I comment those three lines back in, I get the following error
NameError: undefined local variable or method `response' for <RSpec::Core::ExampleGroup::Nested_1::Nested_9:0x007f84c58c31f0>`
How do I examine the results of the post?

My work colleague provided me this answer:
For Sinatra apps the response is in last_response not in response.

Related

RSpec HTTP request tests always passing

So I am having some trouble getting some RSpec tests to FAIL. No matter what I try they are always passing, well, one of the tests works correctly and I verified it can fail if I modify the code it is testing.
I am trying to test the content of JSON responses made to an external API to make sure my controller is sorting these return JSON objects correctly.
Am I missing something here? Why cant I get these to fail?
RSpec.describe 'Posts', type: :request do
describe 'ping' do
it 'returns status 200' do # This text block works correctly.
get "/api/ping"
expect(response.content_type).to eq("application/json; charset=utf-8")
expect(response).to have_http_status(200)
end
end
describe 'get /api/posts' do # all tests below always pass, no matter what.
it 'should return an error json if no tag is given' do
get "/api/posts"
expect(response.content_type).to eq("application/json; charset=utf-8")
expect(response.body).to eq("{\"error\":\"The tag parameter is required\"}")
end
it 'should return a json of posts' do
get "/api/posts?tags=tech" do
expect(body_as_json.keys).to match_array(["id", "authorid", "likes", "popularity", "reads", "tags"])
end
end
it 'should sort the posts by id, in ascending order when no sort order is specified' do
get "/api/posts?tags=tech" do
expect(JSON.parse(response.body['posts'][0]['id'].value)).to_be(1)
expect(JSON.parse(response.body['posts'][-1]['id'].value)).to_be(99)
end
end
it 'should sort the posts by id, in descending order when a descending order is specified' do
get "/api/posts?tags=tech&direction=desc" do
expect(JSON.parse(response.body['posts'][0]['id'].value)).to_be(99)
expect(JSON.parse(response.body['posts'][-1]['id'].value)).to_be(1)
end
end
end
Within the get block of the 'should return an error json if no tag is given' do block I even tried expect(4).to eq(5) and even THIS passed!
Help much appreciated here!
The 'get' should not have a do block. This will cause the test to always pass.
so this:
it 'should sort the posts by id, in descending order when a descending order is specified' do
get "/api/posts?tags=tech&direction=desc" do. # <<< remove this
expect(JSON.parse(response.body['posts'][0]['id'].value)).to_be(99)
expect(JSON.parse(response.body['posts'][-1]['id'].value)).to_be(1)
end # <<< and this from all tests.
end
should look like this:
it 'should sort the posts by id, in descending order when a descending order is specified' do
get "/api/posts?tags=tech&direction=desc"
expect(JSON.parse(response.body['posts'][0]['id'].value)).to_be(99)
expect(JSON.parse(response.body['posts'][-1]['id'].value)).to_be(1)
end

How to verify json response in controller tests?

I have a rails controller that returns only json
def index
if params[:filtered] = 'someValue'
#json = Model.where(some_conditions).to_json
else
#json = Model.where(some_other_conditions).to_json
end
render json: #json
end
What is the correct way to test that the action is returning the expected #json objects?
I've tried the following
describe "GET #index" do
before :each do
get :index, filtered: 'someValue'
end
it { expect( response.body ).to eq 'my expected response' }
end
But I'm getting
Failure/Error: it { expect( response.body ).to eq 'my expected response' }
expected: 'my expected response'
got: "[]"
I'm having trouble determining whether there is a problem in the underlying controller, or whether I've simply written a bad test.
Is response.body the correct way to get the json payload?
Help appreciated!
Both your controller and spec are somewhat off.
You don't need to call to_json on the object that you want to render.
If you use the :json option, render will automatically call to_json
for you.
http://guides.rubyonrails.org/layouts_and_rendering.html
The reason your spec is giving you "[]" is that Model.where(some_conditions) is returning an empty collection. An empty collection renders as an empty array in JSON.
Either the scope does not work as intended or your test setup is flawed. Remember that let variables are lazy loading and you either need to use let! or reference the variable for records to be inserted into the test db.
# polyfill for Rails 4. Remove if you are using Rails 5.
let(:parsed_response) { response.body.to_json }
describe "GET #index" do
# or use fixtures / factories
let!(:model) { Model.create!(foo: 'bar') }
before :each do
get :index, filtered: 'someValue'
end
expect(parsed_response.first["id"].to_i).to eq model.id
end

Rspec Rails 4.2.5 Request test pass with basic http auth

The setup is the following. For each http request the manager sends his credentials in the header(name,pw). These get checked against the entries in the db and if they succeed return the desired user object. How is it possible to implement basic http_auth in the request tests? I would like to add only the password and username and test the return value? Which is the goal of request tests,right? I tried the following without much success:
I created an AuthHelper module in spec/support/auth_helper.rb with
module AuthHelper
def http_login
user = 'test'
pw = 'test'
request.ENV['HTTP_AUTHORIZATION'] =ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
end
end
and use it in the requests/api/user_spec.rb as follows
include AuthHelper
describe "User API get 1 user object" do
before(:each) do
http_login
end
but i receive this error message. How can i fix this and enable my tests to pass http_auth? I read lot of similar topis and questions also here but
they apply mostly to older versions of rspec and rails and are not applying to my case
Thanks in advance!
Failure/Error: request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
NoMethodError:
undefined method `env' for nil:NilClass# ./spec/support
# ./spec/support/auth_helper.rb:5:in `http_login'
# ./spec/requests/api/user_spec.rb:8:in `block (2 levels) in <top (required)>'
Update
I moved the header generation inside a request. I looked up the Auth verb, so i think the assignment should work. I tested the ActionController call in rails console and received a string. I suppose this is also correct.My whole test now looks like this:
describe "User API get 1 user object", type: :request do
it 'Get sends back one User object ' do
headers = {
"AUTHORIZATION" =>ActionController::HttpAuthentication::Basic.encode_credentials("test","test")
# "AUTHORIZATION" =>ActionController::HttpAuthentication::Token.encode_credentials("test","test")
}
FactoryGirl.create(:user)
get "/api/1/user", headers
#json = JSON.parse(response.body)
expect(response).to be_success
# expect(response.content_type).to eq("application/json")
end
end
receiving the following error:
which incudles the line #buf=["HTTP Basic: Access denied.\n"] so access is still denied.
Failure/Error: expect(response).to be_success
expected `#<ActionDispatch::TestResponse:0x000000070d1d38 #mon_owner=nil, #mon_count=0, #mon_mutex=#<Thread::Mutex:0x000000070d1c98>, #stream=#<ActionDispatch::Response::Buffer:0x000000070d1c48 #response=#<ActionDispatch::TestResponse:0x000000070d1d38 ...>,
#buf=["HTTP Basic: Access denied.\n"], #closed=false>, #header={"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "WWW-Authenticate"=>"Basic realm=\"Application\"", "Content-Type"=>"text/html; charset=utf-8", "Cache-Control"=>"no-cache", "X-Request-Id"=>"9c27d4e9-84c0-4ef3-82ed-cccfb19876a0", "X-Runtime"=>"0.134230", "Content-Length"=>"27"}, #status=401, #sending_file=false, #blank=false,
#cv=#<MonitorMixin::ConditionVariable:0x000000070d1bf8 #monitor=#<ActionDispatch::TestResponse:0x000000070d1d38 ...>, #cond=#<Thread::ConditionVariable:0x000000070d1bd0>>, #committed=false, #sending=false, #sent=false, #content_type=#<Mime::Type:0x00000002af78f8 #synonyms=["application/xhtml+xml"], #symbol=:html, #string="text/html">, #charset="utf-8", #cache_control={:no_cache=>true}, #etag=nil>.success?`
to return true, got false
SOLUTION
This test is not polished (yet) but at least it passes now.
describe "User API get 1 user object", type: :request do
it 'Get sends back one User object ' do
#env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
FactoryGirl.create(:user)
get "/api/1/user", {}, #env
JSON.parse(response.body)
expect(response).to be_success
expect(response.status).to eq 200
end
end
Read the error carefully: undefined method `env' for nil:NilClass means request is nil. Are you trying to set the header before a test while you are defining the request later on in the test?
You might want to look at the documentation for an example on how to set headers.
If you're still stuck, post one of your tests as well.
This line looks suspicious:
request.ENV['HTTP_AUTHORIZATION'] =ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
Are you sure that "ENV" should be capitalized? I think it should be written like "env".

How do I get at the params included in an RSpec response to examine it?

I am writing a controller spec (RSpec with Devise), and I know that the response returns the parameter I passed in to it (correctly so), because I see it in the output as this:
...#params={"email" => "maddison_stokes#schumm.org", ...
This is my controller spec:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
login_user
post :create, { email: #users.first[:email] }
expect(response.params[:email]).to include(#users.first[:email])
end
end
end
When I run the test above, I get this error:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: expect(response.params[:email]).to include(#users.first[:email])
NoMethodError:
undefined method `params' for #<ActionController::TestResponse:0x007fa50d1ee6a0>
This is what the entire output of the response looks like:
https://gist.github.com/marcamillion/c2e3f4d0bdae05c2be1f
I tried to paste it here, but SO screamed at me for exceeding the 30,000 character limit. I would have truncated it, but didn't want to remove any info that may be necessary.
What I am trying to do is basically verify that the email in params[:email] contained in the response is the same email I passed to it. I know it is intuitively but I would like to do it programmatically.
undefined method `params' for
ActionController::TestResponse:0x007fa50d1ee6a0
You should use controller.params[:email] instead of response.params[:email]
params are bound to the controller not response.

Default rspec request stub response not working

I've created a test with
rails g integration_test index --webrat
This creates the file:
require 'spec_helper'
describe "Indices" do
describe "GET /indices" do
it "works! (now write some real specs)" do
visit indices_path
response.status.should be(200)
end
end
end
That's all fine, but the only problem is that this test fails even though indices_path exists:
Failure/Error: response.status.should be(200)
NoMethodError:
undefined method `status' for nil:NilClass
So it seems like response is nil.
I can still use page (e.g. page.should have_content works perfectly), but not response. I'd really just like to know that the response status was 200. Can I use something other than response?
If page is available, it looks like you may have capybara instead of webrat. You can try this:
page.status_code.should be 200

Resources