RSpec Controller test converting hash into String - ruby-on-rails

I have a controller spec for my application, which tests the create method on a controller. The create action actually works fine, but the spec is failing. It seems that it is auto converting the the hash POST param into a string.
let(:coupon) { attributes_for(:coupon) }
describe 'POST #create' do
it 'should create a new coupon from params' do
expect {
post :create, :coupon => coupon
}.to change(Coupon, :count).by(1)
end
end
Now, If I do puts coupon it is generating a valid hash of data, and the type is hash. For some reason the controller is receiving a string for params[:coupon]. Only in rspec testing does this happen, when I test in the browse with a POST form it works perfectly fine.
Rspec throws the following message:
NoMethodError:
undefined method `permit' for #<String:0x00000005062700>
Did you mean? print
and if I do puts params[:coupon].class in the controller in rspec it gives me String. Why might it be converting my hash into a string for the POST request, and how can I prevent this ?
I am using Rails 5.0.0 and rspec 3.5.1

This exact same behavior showed up for me recently when testing a JSON API endpoint. Originally I had this as my subject:
subject { put :my_endpoint, **input_args }
and an integer value in input_args was getting translated into a string. The fix was to add format: 'json' as an additional keyword argument to put:
subject { put :my_endpoint, **input_args, format: 'json' }

It seems that it was an issue with the gem open_taobao somehow transforming my post requests in tests.

Related

Rspec - NoMethodError: undefined method `cookies' for Object

Try to add rspec tests to my App and have following error:
NoMethodError:
undefined method `cookies' for #
Here is my simple code:
get :my_action, params: { id: #request.id }
expect(response.status).to eq(200)
But I do not use anywhere cookies in my App project. And why 'coockies' try to executes for my Request model object?
What could it be?
PS Request - it is my model (has :title, :description)
Found error... I have own model Request, and I use instance variable in RSpec test like:
#request = Request.create(....
But RSpec already uses this instant variable:
request
=> #<ActionController::TestRequest:0x007fa8d3b54fb8
#controller_class=RequestsController,
#custom_param_parsers=
The issue was related with rewriting necessary RSpec #request with my data

Rspec-rails 4 error: ActiveModel::ForbiddenAttributesError

I have an application running in rails 4.1 using mongoid as the orm. I created a model called User which has an attribute email. I am using RSpec for tests. I created the following spec
require 'spec_helper'
describe 'User' do
before(:each) do
#attr = {
user: {
email: "rahul#gmail.com"
}
}
end
it "should create a valid User instance" do
param = ActionController::Parameters.new(#attr)
param.require(:user).permit!
User.create!(param)
end
end
when I run the spec, I get the following error
Failure/Error: User.create!(param)
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
I know this is related to strong parameters but couldn't figure out what I am doing wrong.
From the fine manual:
require(key)
[...] returns the parameter at the given key [...]
So saying param.require(:user) does nothing at all to param, it merely does an existence check and returns param[:user].
I think you want to say something more like this:
param = ActionController::Parameters.new(#attr)
User.create!(param.require(:user).permit!)
That usage would match the usual:
def some_controller_method
#user = User.create(user_param)
end
def user_param
param.require(:user).permit!
end
usage in controllers.

Why is my integration test failing for get? "ArgumentError: bad argument (expected URI object or URI string)"

Why is this test giving this error?
Error
1) Error:
PostIntegrationTest#test_should_not_show_comment_box_if_not_logged_in:
ArgumentError: bad argument (expected URI object or URI string)
test/integration/post_integration_test.rb:6:in `block in <class:PostIntegrationTest>'
Code, post_integration_test.rb
require 'test_helper'
class PostIntegrationTest < ActionDispatch::IntegrationTest
test "should not show comment box if not logged in" do
get :show, 'id' => 1 ########### LINE 6
assert_select 'textarea', false, "Comment textarea must not exist if not logged in"
end
Also doesn't work
get :show, {'id' => 1}
get :show, {id: 1}
Reference
This says you can pass arguments. http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-get
This is an example of using parameters to get: http://guides.rubyonrails.org/testing.html#setup-and-teardown
Version
$ rails -v
Rails 4.0.0
:show is not available in Integration tests, the actions are only available in Controller tests. You need to either use _path helper or string representation of your url.
test "should not show comment box if not logged in" do
# Assuming path is /posts. Replace accordingly.
get "/posts/1" ########### LINE 6
assert_select 'textarea', false, "Comment textarea must not exist if not logged in"
end
From the examples given in Rails Testing Guide we see that, in Integration Tests get/post/put/delete has a url as first argument. While Controllers Tests has the name of action.
Its because in controller test minitest knows that of which controller's actions(show, destroy, new, create) is being called, because we specified the that Controller Name in our test class name. And so in Integration Tests we must pass the url.

How to use RSpec with JBuilder?

I'm looking for a clean way to use JBuilder and test the json output with RSpec. The popular way for JSON testing is to implement the as_json method, and then in RSpec compare the received object with the object.to_json method. But a large reason I'm using JBuilder is that I don't want all the attributes that to_json spits out; so this breaks comparison.
Currently with JBuilder I'm having to do the following to test the RSpec results:
1) Create a Factory object: #venue
2) Create a hash inside my RSpec test that contains the "expected" JSON string back
#expected => {:id => #venue.id,:name=>#venue.name..........}
2) Compare the #expected string to the results.response.body that is returned from the JSON call.
This seems simple, except I have objects being rendered with 15+ attributes, and building the #expected hash string is cumbersome and very brittle. Is there a better way to do this?
You should be able to test your Jbuilder views with RSpec views specs. You can see the docs at https://www.relishapp.com/rspec/rspec-rails/v/2-13/docs/view-specs/view-spec.
An example spec for a file located at 'app/views/api/users/_user.json.jbuilder', could be something like this (spec/views/api/users/_user.json.jbuilder_spec.rb):
require 'spec_helper'
describe 'user rendering' do
let(:current_user) { User.new(id: 1, email: 'foo#bar.com') }
before do
view.stub(:current_user).and_return(current_user)
end
it 'does something' do
render 'api/users/user', user: current_user
expect(rendered).to match('foo#bar.com')
end
end
I don't like testing the JSON API through the views, because you have to essentially mimic, in the test, the setup already done in the controller. Also, bypassing the controller, you aren't really testing the API.
In controller tests, however, you'll find that you don't get any JSON returned in the response body. The response body is empty. This is because RSpec disables view rendering in controller tests. (For better or worse.)
In order to have an RSpec controller test of your view rendered JSON API, you must add the render_views directive at the top of your test. See this blog post (not mine), for more detailed information about using RSpec controller tests with Jbuilder.
Also, see this answer.
I have not been able to make RSpec work with the views yet, but I am testing my JSON API via controller RSpec tests. To assist with this process, I am using the api matchers gem. This gem lets you construct RSpec tests such as:
it "should have a 200 code" do
get :list, :format => :json
response.should be_success
response.body.should have_json_node( :code ).with( "200" )
response.body.should have_json_node( :msg ).with( "parameter missing" )
end
This sounds like a good use case for RSpec view specs. Are you using JBuilder for the output of a controller in views?
For example, in spec/views/venues_spec.rb
require 'spec_helper'
describe "venues/show" do
it "renders venue json" do
venue = FactoryGirl.create(:venue)
assign(:venue, venue])
render
expect(view).to render_template(:partial => "_venue")
venue_hash = JSON.parse(rendered)
venue_hash['id'].should == #venue.id
end
end
It's a little clunkier than with say ERB, but you can use binding and eval to run the Jbuilder template directly. E.g. given a typical Jbuilder template app/views/items/_item.json.jbuilder that refers to an instance item of the Item model:
json.extract! item, :id, :name, :active, :created_at, :updated_at
json.url item_url(item, format: :json)
Say you have an endpoint that returns a single Item object. In your request spec, you hit that endpoint:
get item_url(id: 1), as: :json
expect(response).to be_successful # just to be sure
To get the expected value, you can evaluate the template as follows:
item = Item.find(1) # local variable `item` needed by template
json = JbuilderTemplate.new(JbuilderHandler) # local variable `json`, ditto
template_path = 'app/views/items/_item.json.jbuilder'
binding.eval(File.read(template_path)) # run the template
# or, for better error messages:
# binding.eval(File.read(template_path), File.basename(template_path))
expected_json = json.target! # template result, as a string
Then you can compare the template output to your raw HTTP response:
expect(response.body).to eq(expected_json) # plain string comparison
Or, of course, you can parse and compare the parsed results:
actual_value = JSON.parse(response.body)
expected_value = JSON.parse(expected_json)
expect(actual_value).to eq(expected_value)
If you're going to be doing this a lot -- or if, for instance, you want to be able to compare the template result against individual elements of a returned JSON array, you might want to extract a method:
def template_result(template_path, bind)
json = JbuilderTemplate.new(JbuilderHandler)
# `bind` is passed in and doesn't include locals declared here,
# so we need to add `json` explicitly
bind.local_variable_set(:json, json)
bind.eval(File.read(template_path), File.basename(template_path))
JSON.parse(json.target!)
end
You can then do things like:
it 'sorts by name by default' do
get items_url, as: :json
expect(response).to be_successful
parsed_response = JSON.parse(response.body)
expect(parsed_response.size).to eq(Item.count)
expected_items = Item.order(:name)
expected_items.each_with_index do |item, i| # item is used by `binding`
expected_json = template_result('app/views/items/_item.json.jbuilder', binding)
expect(parsed_response[i]).to eq(expected_json)
end
end
You can call the render function directly.
This was key for me to get local variables to work.
require "rails_helper"
RSpec.describe "api/r2/meditations/_meditation", type: :view do
it "renders json" do
meditation = create(:meditation)
render partial: "api/r2/meditations/meditation", locals: {meditation: meditation}
meditation_hash = JSON.parse(rendered)
expect(meditation_hash['slug']).to eq meditation.slug
expect(meditation_hash['description']).to eq meditation.description
end
end

how to POST nested attributes in RSpec integration testing?

I'm writing RSpec integration tests as I convert my spaghetti code to use accepts_nested_attributes_for. I have a snippet like this:
# file: spec/requests/wizard_spec.rb
describe 'POST /wizard with address' do
before(:each) do
#premise_attributes = {
"address"=>"600 Mellow Ave, Mellow Park, CA 94025, USA",
}
end
it 'should succeed' do
post :create, "wizard" => { "premise_attributes" => #premise_attributes }
response.status.should be(200)
end
end
Of course, this fails with:
Failure/Error: post :create, "wizard" => { "premise_attributes" => #premise_attributes }
ArgumentError:
bad argument(expected URI object or URI string)
Is there a method that converts the nested attributes hashes into a POST-able format?
(Related but less important: where is the post method documented or defined? I'd like to see what it really accepts as arguments.)
Instead post :create try use post "/wizard" or nest your specs inside describe WizardController do; end block. Generally you can use method :action syntax only if you're inside describe block for the given controller.
I found this while trying to fix my issue with trying to test put. My post method works though so maybe I can help you out if you still need it. I think your issue is that you're trying to update your attributes as if it was a scalar type variable, but nested attributes are really like an array. Rails generally names them "0", "1", etc., but I'm not sure it matters what the names are as long as they're unique. Give this a try:
#premise_attributes = {
"0" => {"address"=>"600 Mellow Ave, Mellow Park, CA 94025, USA"}
}
(By the way, the problem I'm having is that my update specs are failing because it says something like my address is not unique to borrow your example.)

Resources