How to rework rspec json spec without using .items keyword - ruby-on-rails

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"}}]}

Related

What is map(&:id) in Rails?

My question is not an error, it is for understanding. As I'm new to Rails, I can't read all the code yet.
what does (&:id) do after .map
#user_cnae_classifications = user.cnae_classifications.map(&:id)
what is the difference of .map with it and without it?
in this method call:
UserCnaeClassification.create(
user: #user,
cnae_classification_id: id
)
How do I read that part of the code...
user: #user,
cnae_classification_id: id
are they keys and values?
1 )
You should read some tutorials on map to get acquainted.
https://www.rubyguides.com/2018/10/ruby-map-method
But the short answer is that running user.cnae_classifications.map(&:id) will loop over all cnae_classifications and extract the id from them and put them all into an array. The & character allows for map shorthand to avoid passing an entire block.
From the link above:
2 )
The #create method can accept a key-value hash of known attributes (known to the class in question, in this case that is UserCnaeClassification) to assign upon creation. So you're basically right, they are key-value pairs but they are specific to this class/object. Those same keys might not work on another class/object.
Additional reading: https://guides.rubyonrails.org/active_record_basics.html#create
what does (&:id) do after .map
The syntax map(&:method) is equivalent to:
object.map do |i|
i.method
end
The complete explanation is that the & operator is used to convert any Ruby object that responds to to_proc into a Proc, which encapsulates a block of code. In this case, the Symbol object (:id) is converted into the block of code above.
If you're interested in learning more about it, notice this is pure Ruby, not Rails-specific. Check the documentation for Proc.
In this method call:
How do I read that part of the code...
are they keys and values?
These are keyword arguments. It's a way to name the parameters of a method to explicitly tell the reader what each value should be. Just be aware that the behavior of methods accepting hashes as keyword arguments is deprecated, as seen in this official post.
The .map(&:id) is a shorthand for the longer form of .map { |x| x.id }.
Some interesting things to say: if you're using database (ORM - ActiveRecord), you will see that writing map(&:id) could be helpful. There also exists method called pluck, which does similiar things, but it's a little faster.
Usage:
Also pluck doesn't work with regular Arrays.

Ruby on Rails - using a block parameter as a method call

I'm having trouble with a little Ruby on Rails I'm building and need some help.
I have a Table with 20+ Columns and a corresponding XML File which can be parsed as some sort of hash with a gem. Every key would be mapped to a column and every value would be a data record in said column.
The way I access a specific value in the already parsed XML file is:
filename["crs","inputkeyhere"]
which returns the value, for example "52" or whatever.
What I am trying to do is upload the file, parse it with the gem and give each column the corresponding value.
My table (or model) is called "Attributeset" and I already know how I can access every column:
#attributeset = Attributeset.new
#attributeset.attributes.keys
So my thought process was:
Iterate over all the keys
Pass every key into a block called |a|
Use the rails possibilty to set attributes by calling the corresponding #attributeset.
Set colum attribute to the corresponding xml key
So my code would go something like this:
#attributeset.attributes.keys.each do |a|
#attributeset.a=filename["crs",a]
end
But my problem is, that ruby thinks ".a" is a method and apparently does not evaluate "a" to the block parameter.
I've read through lambdas and procs and whatnot but didn't really understand how they could work for my specific situation.
Coming from bash scripting maybe my thinking might be wrong but I thought that the .a might get evaluated.
I know I can run the block with yield, but this only works in methods as far as I know..
Any help is appreciated.
Thanks and stay healthy,
Alex
Thanks for the input!
I wanted to make it as clean as possible, and not using any temporary hashes to pass arguments.
I've found the method
write_attribute
which can be used like this:
#attributeset.write_attribute(a, xmp["crs",a])
worked perfectly for me.
You can use []= method to set values dynamically:
#attributeset.attribute_names.each do |attribute|
#attributeset[attribute] = filename["crs", attribute]
end

RSpec matching response

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

conditional match on rails rspec

I want to write a test on helper method which fetches data from external service based on an id. So there is an uncertainty whether the value will be returned or nil. But if value is returned, id of returned value must be equal to given id. Is there a way to achieve this?
expect(record).to be_nil.or expect(record.id).to eq(deal.user_id)
however it seems the or condition does not work the way I think. I am new to RoR. Might be missing any obvious way to do it.
You can write ruby code in specs too:
if record != nil
expect(record.id).to eq(deal.user_id)
end
or combine matchers:
expect(record).to be_nil.or(eq(deal.user_id))

How do I expect a method to be run with specific ActiveRecord parameters

Using Mocha on Rails 4.2.
I'm testing a method that it should make a call to another method with the correct parameters. These parameters are ActiveRecord objects that it calls up from the database. Here is the key line in my test:
UserMailer.expects(:prompt_champion).with(users(:emma), [[language, 31.days.ago]]).once
Both users(:emma) and language are ActiveRecord objects.
Even though the correct call is made, the test fails because the parameters don't match the expectations. I think this might be because it's a different Ruby object each time a record is pulled up from the database.
I think one way around it is to see what method is being used in my code to pull up the records and stub that method to return mocks, but I don't want to do this because a whole bunch of Records are retrieved then filtered down to get to the right one, mocking all those records would make the test way too complex.
Is there a better way of doing this?
You could use block form of allow/expect.
expect(UserMailer).to receive(:prompt_champion) do |user, date|
expect(user.name).to eq "Emma"
expect(date).to eq 31.days.ago # or whatever
end
Sergio gave the best answer and I accepted it. I discovered the answer independently and found out along the way that I needed to return a mock from the ActionMailer method to make everything work properly.
I think it best to post here my complete test here for the sake of any other hapless adventurer to come this way. I'm using Minitest-Spec.
it 'prompts champions when there have been no edits for over a month' do
language.updated_at = 31.days.ago
language.champion = users(:emma)
language.save
mail = mock()
mail.stubs(:deliver_now).returns(true)
UserMailer.expects(:prompt_champion).with do |user, languages|
_(user.id).must_equal language.champion_id
_(languages.first.first.id).must_equal language.id
end.once.returns(mail)
Language.prompt_champions
end
You could use an RSpec custom matcher and compare expected values in that function.

Resources