I have this test:
it "saves the notification id in the referral for future reference" do
expect { subject.perform(*args) }
.to change(referral, :notification_id).from(nil).to(customer_notification_delivery.id)
end
And the code that it runs on top is:
if notification.present?
referral.update(friend_customer_notification_delivery_id: notification.id)
end
I added a few debug messages, to check on them after firing the test, to ensure that this condition was being met, and the code was being run, and I got true for both
p notification.present?
p referral.update(friend_customer_notification_delivery_id: customer_notification_delivery.id)
Anything I am missing? Why the update returns true, but the value is not getting updated on the test?
The output I get:
expected #notification_id to have changed from nil to 5, but did not change
referral in your test and referral in your object-under-test are two different objects, I'm willing to bet. Changes to one do not affect the other. referral in the test does not magically pull up updates from the related database record made by some other code.
I normally do it like this
it "saves the notification id in the referral for future reference" do
expect { subject.perform(*args) }
.to change{ referral.reload.notification_id }.from(nil).to(customer_notification_delivery.id)
end
Related
In a recursive rails model I have a validation that the model cannot reference itself:
validates :parent_entity, exclusion: { in: ->(entity) { [entity] } }
This is successful and the exclusion error with the correct message is set. I can approve this via rails console.
In a Rspec test I want to check if the appropriate exclusion error is added:
it 'parent_entity cannot be same entity as child_entity' do
#child_entity1.parent_entity = #child_entity1
#child_entity1.valid?
expect(#child_entity1.errors.added?(:parent_entity, :exclusion)).to be_truthy
end
The test fails returning a falsey value in the expect.
The preceding approach works flawlessly for e. g. blank errors but not with exclusion. If I exchange ':exclusion' in the test with the resolved error message 'is reserved', I can get it working but this is not what I want and should do.
Taken from the rails docs for the added? method:
If the error message requires options, then it returns true with the
correct options, or false with incorrect or missing options.
Therefore the options have to fully match. The necessary option for :exclusion is the value and is passed as the last parameter:
#child_entity1.errors.added?(:parent_entity, :exclusion, value: #child_entity1)
I have report object called SiegeReport, that makes some calculations and returns integer object. When there is no warrior in the building, then siege_ability equals 0. The code itself is not important here, because it works fine in console and in application. Factories made by factory_bot work ok in all the other examples. I just have problem with testing the method:
require 'rails_helper'
RSpec.describe Reports::SiegeReport do
subject(:siege_report) { Reports::SiegeReport.new(building: building).call }
let(:building) { create(:building, granary: 100) }
let(:clan) { create(:clan) }
context 'with 1 infantry' do
let(:warrior) { create(:warrior, clan_id: clan.id, building_id: building.id) }
it 'returns 9' do
expect(siege_report).to eq(9)
end
end
end
RSpec returns:
Reports::SiegeReport siege ability with 1 infantry returns 9
Failure/Error: expect(siege_report).to eq(9)
expected: 9
got: 0
(compared using ==)
I checked it with pry and warrior object is valid, even building.warriors returns warrior, but in attributes number of warriors is still 0. The very same example works when i type it manually in console. How can I make RSpec update building attributes before testing?
Ok, I found solution but is really not elegant:
it 'returns 9' do
warrior.save
expect(siege_report).to eq(9)
end
It works but it seems to me that it is not the proper way of testing. Still, I can't find a better solution.
I am working on a Ruby problem called "Speaking Grandma" where I need to create a method that should should take in a string argument containing a phrase and check to see if the phrase is written in all uppercase: if it isn't, then grandma can't hear you. She should then respond with (return) HUH?! SPEAK UP, SONNY!.
However, if you shout at her (i.e. call the method with a string argument containing a phrase that is all uppercase, then she can hear you (or at least she thinks that she can) and should respond with (return) NO, NOT SINCE 1938!.
I wrote the following code:
def speak_to_grandma(input)
if
input != input.upcase
puts 'HUH?! SPEAK UP, SONNY!'
else
puts 'NO, NOT SINCE 1938!'
end
end
When I run RSpec, I fail both tests. It gives the following message:
Failure/Error: expect(speak_to_grandma('Hi Nana, how are you?')).to eq 'HUH?! SPEAK UP, SONNY!'
expected: "HUH?! SPEAK UP, SONNY!"
got: nil
and
Failure/Error: expect(speak_to_grandma('WHAT DID YOU EAT TODAY?')).to eq "NO, NOT SINCE 1938!"
expected: "NO, NOT SINCE 1938!"
got: nil
(compared using ==)
I have no idea what I am doing wrong here. Can anyone help?
The speak_to_grandma method returns the return value of the puts method, being the last line in the method. The return value of puts is nil(Why are all my puts returning =>nil?)
The eq method in Rspec checks the return value of a method. The string is output to the screen with puts, but the return value is nil, and that's what Rspec is checking for.
If you remove puts, the tests should pass, because the string will be the return value of the method. But the correct way would be to test it with the output method in Rspec. Write your test like this:
expect { speak_to_grandma('WHAT DID YOU EAT TODAY?') }.to output("NO, NOT SINCE 1938!").to_stdout
I have a little function to make a customer_nr with
id.to_s.rjust 8, "0"
This gives me a 8 diget number with the id and before filled with 0 until I have 8 diggets.
How can I test this with rspec? The first test works, but the second on is wrong since the id is interactive. Any Idea how to test the eq? FactoryGirl creates a user with FactoryGirl.create(:user). This works if the created user gets an single digget id.
When it gets a more diggets id then the test fails.
Like expected: "00000002398" got: "00002398"
describe "#customer_nr" do
it "should have 8 digits" do
user.customer_nr.length.should eq 8
end
it "should use the user_id" do
user.customer_nr.should eq "0000000#{user.id}"
end
end
best regard
denym
After my comment and your edit issue is easy. Everything is correct but your user have id 2398. You can make test in irb
2398.to_s.rjust 8, "0"
give exactly what test return. In test your db created records and destroy after tested but id still increment. You can make:
user.customer_nr.should eq "0000000#{user.id}".split(//).last(8).join
or like toro2k wrote in his comment.
I would like to write a request spec that verifies the loading and execution of javascript on a given page without any errors.
I know I can add something to the DOM at the end of my JavaScript file and assert the presence of this content, but that feels like a hack and forces me to pollute my code for testing reasons. I'd prefer to do something along the lines of.
visit some_path
page.should succesfully_run_javascript
You can achieve this with the following piece of code in your tests.
page.driver.console_messages.each { |error| puts error }
expect(page.driver.console_messages.length).to eq(0)
The first line prints out an errors, handy for seeing what's going on. The second line causes the test to fail.
One way that's potentially not very polluting is to collect any errors by adding something like this to head (keep in mind this will override any onerror handler you may already have):
<script>
window.errors = [];
window.onerror = function(error, url, line) {
window.errors.push(error);
};
</script>
You could then do something like:
page_errors = page.evaluate_script('window.errors')
and assert on the array being empty. You could output the errors otherwise...
Note: It's important to add the scriptlet to head (potentially as the first scriptlet/script tag) as it needs to be one the first executed scripts.
A problem with using page.driver.console_messages is that it doesn't clear between tests. So if you want to only assert that there are no errors on a particular test the spec might fail due to a previous test.
You can scope these errors to a particular spec by saving the last console.log timestamp.
Helper method:
def assert_no_js_errors
last_timestamp = page.driver.browser.manage.logs.get(:browser)
.map(&:timestamp)
.last || 0
yield
errors = page.driver.browser.manage.logs.get(:browser)
.reject { |e| e.timestamp > last_timestamp }
.reject { |e| e.level == 'WARNING' }
assert errors.length.zero?, "Expected no js errors, but these errors where found: #{errors.join(', ')}"
end
And then use it like:
def test_somthing_without_js_errors
assert_no_js_errors do
# TODO: Write test
end
end