How to test an if-condition not been excuted? - ruby-on-rails

I'm new to Rspec. I have a code like this:
if #context.persona == :system && #context.scopes&.include?(SEARCH_SCOPE)
return <something>
end
I want to write a unit test to confirm the #context.scopes&.include?(SEARCH_SCOPE) is not being executed when #context.persona is not :system. Here is what I wrote:
context 'when persona is system' do
let(:persona) { :system }
it 'checks the scope' do
allow(context).to receive(:scopes)
expect(context).to have_received(:scopes)
end
end
context 'when persona is not system' do
let(:persona) { :user }
it 'checks the scope' do
allow(context).to receive(:scopes)
expect(context).not_to have_received(:scopes)
end
end
The second test passed, but the first test failed with:
Failure/Error: expect(context).to have_received(:scopes)
(Double (anonymous)).scopes(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Could someone help me? I googled it before but didn't see anything helpful. I'm sorry if it is duplicated.

Not a direct answer to your question, but you are falling into the pit of testing implementation, instead of behaviour. Don't do that.
Your test shouldn't care about this:
expect(context).not_to have_received(:scopes)
Instead, your test should only be doing something like this:
context 'when persona is system and scopes includes SEARCH_SCOPE' do
let(:persona) { :system }
let(:scopes) { ... }
it 'returns <something>' do
expect(the_method_being_invoked).to eq(<something>)
end
end
context 'when persona is not system' do
let(:persona) { :user }
let(:scopes) { ... }
it 'returns <something-else>' do
expect(the_method_being_invoked).to eq(<something-else>)
end
end
context 'when scopes is empty' do
let(:persona) { :user }
let(:scopes) { nil }
it 'returns <something-else>' do
expect(the_method_being_invoked).to eq(<something-else>)
end
end
Why? Because when you refactor code, and the implementation changes, you don't want specs to start failing unless the behaviour has also changed.
You should usually even be able to write the test before writing the method -- therefore having no knowledge of its implementation details.

Related

How to pass variables with the same name in rspec's shared_examples?

Let's imagine I have some tests and want to use shared examples, e.g.:
RSpec.shared_examples "shared examples" do
let(:x) { "" }
it "should be equal to 5" do
expect(x).to eq(5)
end
end
and then use it like:
describe "my tests" do
let(:x) { 5 }
it_behaves_like "shared examples" do
let(:x) { x }
end
end
I know that I can do that implicitly, without passing let(:x) { x } to the child block and everything will work. But what I'm trying to achieve - is to add more clarity to my tests.
So the question is: how to pass a variable (override if you want) with the same name without falling into maximum call stack error to a child shared examples block?
Please let me know if my approach is not right in general.
You just need to define the variable inside blocks, let is lazy-evaluated, so it is not evaluated until the first time
the method it defines is invoked, but they need to be inside blocks like context, something like this:
RSpec.describes "shared examples" do
context 'when x has value' do
let(:x) { 5 }
it "should be equal to 5" do
expect(x).to eq(5)
end
end
context 'when x is empty' do
let(:x) { '' }
it "should be empty" do
expect(x).to eq('')
end
end
end

What a test should test with Rspec in Ruby on Rails application

I'm beginner in Rspec, and actually we asked me to do Rspec test for somes method which are already build but which never have been test (they don't write test before building the method).
And now I'm struggling to know how can I test my method, here is example:
class ConnectorJob < ActiveJob::Base
queue_as :connector
def perform(tenant_id = nil, debug = false)
tenant_to_sync = Tenant.to_sync(tenant_id)
return if tenant_to_sync.empty?
tenant_to_sync.each do |tenant|
service = MyAPP::ContactToSync.new(tenant, debug).call
if service.success?
ConnectorService::Synchronization.new(
tenant, service.data, debug
).call
end
end
end
end
What should I test on this? Should I test the return value is correct or if other method are well called?
Here is what I tried to do
require "rails_helper"
RSpec.describe ConnectorJob, type: :job do
it 'is in connector queue' do
expect(ConnectorJob.new.queue_name).to eq('connector')
end
describe 'perform' do
let (:tenant) { create(:tenant) }
let (:job) { ConnectorJob.new.perform(tenant.id) }
context 'with empty tenant' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to eq nil }
end
context 'with tenant' do
it { expect(ConnectorJob.new.perform(tenant.id)).to eq job }
end
end
end
As you can see my last test doesn't have sense but I have no idea what I should write on my Rspec for anticipate the result of this method.
If I check my Rspec coverage, Rspec is telling me I cover 100% of my method but I'm not sure that is correct.
I hope I'm clear, feel free to ask me more details.
Thank you all
I think you should test final result, I mean result after calling
ConnectorService::Synchronization.new(...).call and test three cases, e.g. if this call create new user, you should test it:
If tenant_to_sync.empty? == true
context 'with empty tenant' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(0) }
end
If service.success? == false
context 'MyAPP::ContactToSync return false' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(0) }
end
If service.success? == true
context 'success' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(1) }
end
It should be enough to cover all scenarios.

Test if a method is called inside a module method

I have the following in my module:
module SimilarityMachine
...
def answers_similarity(answer_1, answer_2)
if answer_1.compilation_error? && answer_2.compilation_error?
return compiler_output_similarity(answer_1, answer_2)
elsif answer_1.compilation_error? || answer_2.compilation_error?
return source_code_similarity(answer_1, answer_2)
else
content_sim = source_code_similarity(answer_1, answer_2)
test_cases_sim = test_cases_output_similarity(answer_1, answer_2)
answers_formula(content_sim, test_cases_sim)
end
end
...
end
I would like to test these "if conditions", to ensure that the right methods are called (all these methods are from SimilarityMachine module). To do that, I have:
describe SimilarityMachine do
describe '#answers_similarity' do
subject { answers_similarity(answer_1, answer_2) }
let(:answer_1) { create(:answer, :invalid_content) }
context "when both answers have compilation error" do
let(:answer_2) { create(:answer, :invalid_content) }
it "calls compiler_output_similarity method" do
expect(described_class).to receive(:compiler_output_similarity)
subject
end
end
end
With both answers created I go to the right if (the first, and I'm sure of that because I tested before). However, my result is:
1) SimilarityMachine#answers_similarity when both answers have compilation error calls compiler_output_similarity method
Failure/Error: expect(described_class).to receive(:compiler_output_similarity)
(SimilarityMachine).compiler_output_similarity(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
What am I doing wrong?
I would check out Testing modules in rspec other questions related to testing modules.
I'm not completely clear on this, but in general, modules don't receive method calls. They are collections of methods that have to be "mixed in" through the extend method and the like.
Here's an example how to test a module method in isolation, taken from https://semaphoreci.com/community/tutorials/testing-mixins-in-isolation-with-minitest-and-rspec:
describe FastCar
before(:each) do
#test_obj = Object.new
#test_obj.extend(Speedable)
end
it "reports the speed" do
expect(#test_obj.speed).to eq "This car runs super fast!"
end
end

rspec testing callback worked

i am trying to test some callback before_save logic. But i am stack in this dark place where i cant figure out solution.
I have this method which updates some attributes before save:
def order_item_positions
Place.item_positions_reorder(item_position, city_id).each do |place|
new_item_position = place.item_position + 1
place.update_attributes(item_position: new_item_position)
end
end
What that method does, is changes all records above to +1 position!
and than i want to test it using rspec something like this:
describe "places order" do
let(:city){FactoryGirl.create(:city)}
let(:place){FactoryGirl.create(:place, city_id: city.id)}
let(:place_sec){FactoryGirl.create(:place, city_id: city.id)}
context "when placed before other record" do
it "should be placed before" do
place_sec.item_position = 1
place.item_position = 1
expect{
...somehow saving and triggering callback! //dont know how to do that :/
}.to change(place_sec, :item_position).from(1).to(2)
end
end
end
Any help would be much appreciated! :)
You should build model and then save it, i think:
describe "places order" do
let!(:city) { FactoryGirl.create(:city) }
let!(:place) { FactoryGirl.create(:place, city_id: city.id) }
let!(:place_sec) { FactoryGirl.build(:place, city_id: city.id) }
context "when placed before other record" do
it "should be placed before" do
place_sec.item_position = 1
place.item_position = 1
expect(place_sec.save).to change(place_sec, :item_position).from(1).to(2)
end
end
end
You didn't mention in what model you have this before_save method order_item_positions. So what should you save to call it. Just build this model, and then save.
A simple call to .save should do it:
expect{
place_sec.save
}.to change(place_sec, :item_position).from(1).to(2)

Contexts in rspec

I have a basic doubt.
If the rspec file contains many contexts:
describe Name do
context "number1" do
.......
.......
end
context "number 2" do
.......
.......
end
context "number 3" do
.......
.......
end
How should the functions from each of the contexts be described in the .rb file? Should they be in the same class or different class? Is there any book I can read to improve my knowledge about this?
The structure I use when defining rspec files (based on reading I've done on rspec) is that you use describes to describe specific functions, and context to talk about a specific context of state and/or path through the function.
Example class:
class MyClass
def self.my_class_method(bool)
if bool == true
return "Yes"
else
return "No"
end
end
def my_instance_method
today = Date.today
if today.month == 2 and today.day == 14
puts "Valentine's Day"
else
puts "Other"
end
end
end
As you can see, I've defined a class method and an instance method that do really silly and random functions. But the point is this: the class method will do something different based on the argument, and the instance method will do something different based on some outside factor: you need to test all these, and these are different contexts. But we will describe the functions in the rspec file.
Rspec file:
describe MyClass do
describe ".my_class_method" do
context "with a 'true' argument" do
it "returns 'Yes'." do
MyClass.my_class_method(true).should eq "Yes"
end
end
context "with a 'false' argument" do
it "returns 'No'." do
MyClass.my_class_method(false).should eq "No"
end
end
end
describe "#my_instance_method" do
context "on Feb 14" do
it "returns 'Valentine's Day'." do
Date.stub(:today) { Date.new(2012,2,14) }
MyClass.new.my_instance_method.should eq "Valentine's Day"
end
end
context "on a day that isn't Feb 14" do
it "returns 'Other'." do
Date.stub(:today) { Date.new(2012,2,15) }
MyClass.new.my_instance_method.should eq "Other"
end
end
end
end
So you can see the describe is for saying what method you're describing, and matches up with the name of a method in your class. The context is used to evaluate different conditions the method can be called in, or different states that affect the way the method works.
Hope this helps!

Resources