how do I use rspec to test instance methods in ruby? - ruby

I have an instance method that would be invoked after creating a new instance of the class.
How do I test it in Rspec? I use the following and get an error:
let(:schedule) { ScheduleKaya.new('test-client-id') }
let(:schedule) { schedule.create_recurring_event('test-keyword', 'slack') }
In other words, I want to create the instance. Then I want to apply a method create_recurring_event.
My test wants to check if it assigned the variables to the instance.
it "has #keyword = test-keyword" do
expect(schedule.keyword).to eq('test-keyword')
end
Because it makes a database call, I want to check the response back from the call to see if I get a status = 200.
But I can't seem to both create the instance and then apply the method.
Question:
What is the right way to test for an instance method, one that is applied after creating a new instance.

A let block acts like a method and returns the return value of the last statement. Therefore just write both into the same block and ensure that the right value is returned:
let(:schedule) do
schedule_kaya = ScheduleKaya.new('test-client-id')
schedule_kaya.create_recurring_event('test-keyword', 'slack')
schedule_kaya
end
Or you can use tap:
let(:schedule) do
ScheduleKaya.new('test-client-id').tap do |schedule_kaya|
schedule_kaya.create_recurring_event('test-keyword', 'slack')
end
end

I suggest FactoryGirl together with Rspec, if you are on Railsfactory_girl_rails, looks like below:
it "has #keyword = test-keyword" do
schedule = Factory(:shcedule, keyword: "has #keyword = test-keyword")
expect(schedule.keyword).to eq('test-keyword')
end

Related

Rspec how to mock function call through instance method

I am trying to test a method which uses instance variable to call featureEnabled method. I am trying to write a rspec unit test for this. I am using below setup in the allow statement. Not sure how else to do this
exception: => #<NoMethodError: undefined method `feature_enabled?' for nil:NilClass>
Api.controllers :Customers do
#domain = current_account.domain
def main
t1 = #domain.featureEnabled?("showPages")
blah
Test:
RSpec.describe ApiHelpers do
describe "#find_matching_lists" do
let(:domain) { Domain.new }
it "madarchod3" do
allow(domain).to receive(:featureEnabled?).with("showPages").and_return(true)
end
end
Variables defined in a block are local to this block, they do not exist outside of it.
The problem you're having is that the domain variable you create with let is only visible inside the block passed to describe. Your test must be defined in that describe block for it to access this variable, ie this should work:
describe "#main" do
let(:domain) { Domain.new }
it "check if feature enabled" do
allow(domain).to receive(:featureEnabled?).with("showPages").and_return(true)
end
end

How do I use rspec mock methods from inside a custom DSL

I'm writing a testing library that works on top of rspec. I have a custom dsl that looks like this:
rast Worker do
prepare do |day_type, dow|
allow(subject).to receive(:holiday?) { day_type == 'Holiday' }
allow(subject).to receive(:dow) { dow }
end
execute do
result subject.goto_work?
end
end
The two allow statements do not work because they are inside my custom DSL rast with the method prepare. How can I make it work?
Inside the execute method I invoke this prepare block like this:
def execute
prepare_block = #prepare_block
RSpec.describe "test" do
prepare_block&.call(*params)
...
I don't have the whole picture, but at a guess and off the top of my mind, you may fare better with something like
RSpec.describe "test" do
instance_eval(prepare_block, *params) if prepare_block
end
instance_eval will evaluate the block in the context of the receiver (so whatever self is inside the describe block).
If you just do prepare_block.call, it won't have access to any methods defined in the context where it happened to be called from, as you found out.
Good luck!

How to stub a instance method for a specific instance?

I have below case,
class Schools::Status
def initialize(school)
#school = school
end
def active?
true
end
end
Now, I want to stub active? method for a specific school.
One way to have like this
Schools::Status.new(school).stubs(:active?).returns(false)
But my use case is different, I have search result of schools and I want to filter that result based on active? value as below:
schools.select { |s| Schools::Status.new(school).active? }
In above case, specifically I want to stub active? for certain instance.
Just monkey-patch your class in the spec.The more rspec-way would be to use any_instance with stub but the problem is you cannot get access to the self of the stubbed instance so you practicly have no information about the school and you cannot access it in that block.
Example:
Status.any_instance.stub(:active?) { # no way to access school }
I found answer on myself and putting here so that others can be benefited
Lets say, I have school for which active? method of Schools::Status is to be stubbed.
In order to achieve this,
First we need to stub new method of Schools::Status so that it will return Schools::Status instance which we want and it can be done as below -
status = Schools::Status.new(school)
# now whenever Schools::Status instance getting created for ours school
# it will return our defined status instance
Schools::Status.stubs(:new).with(school).returns(status)
Secondly, we have to stub active? method for status instance -
status.stubs(:active?).returns(false)
Now, filter will reject specified school for which active? method returns false

rspec should received x.times a message [duplicate]

I have something like:
value = nil
if some_condition
value =my_object.do_stuff()
end
And in my test, I have the follwing:
MyObject.any_instance.should_receive(:do_stuff)
However, I'd like to just test that the method was called, and have it execute the original code. I'd like to NOT have to do it like:
MyObject.any_instance.should_receive(:do_stuff).and_return(:some_dummy_value)
Is there a way of doing that?
There is and_call_original method:
MyObject.any_instance.should_receive(:do_stuff).and_call_original
See https://github.com/rspec/rspec-mocks#delegating-to-the-original-implementation
I believe, that it's better to create object by FactoryGirl and than to test it. You can read how to make factories and so on.
Example:
FactoryGirl.define do
factory :my_object do
[*here is your attributes*]
end
end
So, after you created a factory, you should to create test where this method used and write this:
my_object = FactoryGirl.create(:my_object)
my_object.should_receive(:do_stuff)
Inside your code you will do that "do_stuff" with your "my_object" when u will run test.

How to return the receiver instance's self from should_receive block

I'd like to have instance methods of a class return self, and be init with another class instance self.
However I'm struggling to see how to spec this succintly:
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
end
When running this spec, this ensures that the next method is called on true instead of the Api instance, as expected, that is the return value of the block. Example:
class Cli
def eg
api = Api.new(self)
api.blowup # undefined method for true
end
end
I'd really like the block to return the Api instance self without invoking another call to Api.new(...) in the spec, the example below does this and to my mind a non-rspec reader would wonder why the spec passes when clearly Api.new(...) has been called more than once.
Can anyone suggest how best to do this?
Current solution:
This reads like ::Api.new(...) is called thrice: once to create api, once to create cli, once to create start. Yet the spec of one call passes. I understand why and that this is correct, so not a bug. However I'd like a spec that a reader not familiar with rspec could scan and not have the impression that Api.new has been called more than once. Also note that ...once.and_return(api){...} does not work, the block needs to return api in order to pass.
let(:cli){ ::Cli.start(['install']) }
let(:start){ ::Cli.start(['install']) }
it 'is the API' do
api = ::Api.new(cli)
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
api
end
start
end
You can save the original method (new) in a local variable and then use it to return the new api from within the block:
original_method = ::Api.method(:new)
::Api.should_receive(:new).once do |arg|
arg.should be_an_instance_of(::Cli)
original_method.call(arg)
end
This will run the expectation, checking that the argument is an instance of ::Cli, and then return the value from the original method (i.e. the api).

Resources