RSpec matching response - ruby-on-rails

I am using Ruby on Rails 5 and Rspec.
My test is like
expect(json_response['data']['body']).to match(/'["can't be blank"]'/)
I am getting error
expected ["can't be blank"] to match /'["can't be blank"]'/
I was wondering, how to fix it ? Hope it is clear.

Try the match_array helper method.
expect(json_response['data']['body']).to match_array(["can't be blank"])

Reading the test failure, the JSON response returns an array with a string in it: ["can't be blank"]. Seems like a fine use case for testing equality directly:
expect(json_response['data']['body']).to eq(["can't be blank"])
match_array will work, but it "disregards differences in the ordering between the actual and expected array." As an array with one item only, that feature isn't necessary here.
contains_exactly/match_array docs

Related

How to rework rspec json spec without using .items keyword

Can anyone tell me the updated way to write this rspec spec? It uses the keyword "items" which has been deprecated and I can't figure out how to properly do it. When I include the matchers gem, I get a conflict about which rspec version to use so I'd just like to avoid its use all together.
This is the spec for counting how many courses I see in the index when I create 4:
expect(courses_response[:name]).to have(4).items
A sample string I am matching is:
{:courses=>[{:courses=>{:id=>1, :name=>"Wilford Rolfson"}}, {:courses=>{:id=>2, :name=>"Mabel Jacobi"}}, {:courses=>{:id=>3, :name=>"Madison Beahan"}}, {:courses=>{:id=>4, :name=>"Miles Corwin"}}]}
I have tried:
expect(courses_response.length).to eq(4)
That yields "1" instead of "4"
These three yield nil:
expect(courses_response[:name]).to eq(4)
expect(courses_response[:id]).to eq(4)
expect(courses_response[:row]).to eq(4)
Thanks for any help you can give!
If I understand it correctly, because your returned object is a hash with a single key (:courses) and value (an array containing the courses), you need to check the length of that array like so:
expect(courses_response[:courses].length).to eq(4)
This is assuming:
courses_response = {:courses=>[{:courses=>{:id=>1, :name=>"Wilford Rolfson"}}, {:courses=>{:id=>2, :name=>"Mabel Jacobi"}}, {:courses=>{:id=>3, :name=>"Madison Beahan"}}, {:courses=>{:id=>4, :name=>"Miles Corwin"}}]}

All fixtures in rails

I guess I am missing something very very obvious, but in my RSpec tests I want to do something like this:
it "should assign all channels to #channels" do
get :index
assigns(:channels).should eq(channels(:all))
end
As you can see I want all channels in an array for my tests so I don't have to fix all my tests when I add a new fixture in the future.
channels(:all) is not working and channels.kind_of? Array is true and empty by default.
Any suggestions?
What you might need is:
assigns(:channels).should eq(Channel.all)

Nested model error messages

I am using Ruby on Rails 3.0.9 and I am trying to validate a nested model. Supposing that I run validation for the "main" model and that generates some errors for the nested model I get the following:
#user.valid?
#user.errors.inspect
# => {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
How you can see the RoR framework creates an errors hash having following keys: account.firstname, account.lastname, account. Since I would like to display error messages on the front-end content by handling those error key\value pairs with JavaScript (BTW: I use jQuery) that involves CSS properties I thought to "prepare" that data and to change those keys to account_firstname, account_lastname, account (note: I substitute the . with the _ character).
How can I change key values from, for example, account.firstname to account_firstname?
And, mostly important, how I should handle this situation? Is what I am trying to do a "good" way to handle nested model errors? If no, what is the common\best approach to do that?
I've made a quick Concern which shows full error messages for nested models:
https://gist.github.com/4710856
#1.9.3-p362 :008 > s.all_full_error_messages
# => ["Purchaser can't be blank", "Consumer email can't be blank", "Consumer email is invalid", "Consumer full name can't be blank"]
Some creative patching of the Rails errors hash will let you achieve your aim. Create an initializer in config/initalizers, let call it errors_hash_patch.rb and put the following in it:
ActiveModel::Errors.class_eval do
def [](attribute)
attribute = attribute.to_sym
dotted_attribute = attribute.to_s.gsub("_", ".").to_sym
attribute_result = get(attribute)
dotted_attribute_result = get(dotted_attribute)
if attribute_result
attribute_result
elsif dotted_attribute_result
dotted_attribute_result
else
set(attribute, [])
end
end
end
All you're doing in here is simply overriding the accessor method [] to try a little harder. More specifically, if the key you're looking for has underscores, it will try to look it up as is, but if it can't find anything it will also replace all the underscores with dots and try to look that up as well. Other than that the behaviour is the same as the regular [] method. For example, let's say you have an errors hash like the one from your example:
errors = {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
Here are some of the ways you can access it and the results that come back:
errors[:account] => ["is invalid"]
errors[:"account.lastname"] => ["is too short", "can not be blank"]
errors[:account_lastname] => ["is too short", "can not be blank"]
errors[:blah] => []
We don't change the way the keys are stored in the errors hash, so we won't accidentally break libraries and behaviours that may rely on the format of the the hash. All we're doing is being a little smarter regarding how we access the data in the hash. Of course, if you DO want to change the data in the hash, the pattern is the same you will just need to override the []= method, and every time rails tries to store keys with dots in them, just change the dots to underscores.
As to your second question, even though I have shown you how to do what you're asking, in general it is best to try and comply with the way rails tries to do things, rather than trying to bend rails to your will. In your case, if you want to display the error messages via javascript, presumably your javascript will have access to a hash of error data, so why not tweak this data with javascript to be in the format that you need it to be. Alternatively you may clone the error data inside a controller and tweak it there (before your javascript ever has access to it). It is difficult to give advice without knowing more about your situation (how are you writing your forms, what exactly is your validation JS trying to do etc.), but those are some general guidelines.
I had the same problem with AngularJs, so I decided to overwrite the as_json method for the ActiveModel::Errors class in an initializer called active_model_errors.rb so that it can replace . for _
Here is the initializer code:
module ActiveModel
class Errors
def as_json(options=nil)
hash = {}
to_hash(options && options[:full_messages]).each{ |k,v| hash[k.to_s.sub('.', '_')] = messages[k] }
hash
end
end
end
I hope it can be helpful for someone
I'm not sure but I think you can't change that behavior without pain. But you could give a try to solutions like http://bcardarella.com/post/4211204716/client-side-validations-3-0

Is there a way to make RSPec's error messages be more insightful?

I am often at a loss when trying to debug, since RSpec only tells me that
expected redirect to "/user_session/new", got no redirect
expected success? to return true, got false.
but since It doesnt tell me what happened instead its hard to debug,
so is this something built in to RSpec and cannot be changed or is it a configuration thing of some kind.
Test frameworks generally permit you to assert that an actual result is equal to an expected result, and when that assertion fails, will print out the assertion as well as the expected and actual results.
There's not much more they can do. What you seem to be looking for is magical intuition on the part of software.
Try something like:
response.status_code.should == 200
This compares the actual status_code with the expected status_code, asserting their equality, and printing both if the assertion fails.

Rails: problem setting expectations on mock model in RSpec

I am trying to set expectations on a mocked ActiveRecord model. I have created the following example, which should pass based on the documentation I can find.
it "should pass given the correct expectations" do
payment = mock_model(Payment)
payment.should_receive(:membership_id).with(12)
payment.membership_id = 12
end
It is failing with the error "...Mock 'Payment_1004' received unexpected message :membership_id= with (12)"
I realize I am testing the mocking framework, I am just trying to understand how to setup expectations.
You're setting the expectation on the wrong method name - :membership_id is the "getter", :membership_id= is the "setter". The correct line would be:
payment.should_receive(:membership_id=).with(12)
Another useful "out" here -- if one doesn't care about the id key -- is to do something like the following:
mock_model(Payment,:[]= => nil, :save=> nil)
...or maybe just
mock_model(Payment,:[]= => nil)
Lille

Resources