rspec should received x.times a message [duplicate] - ruby

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.

Related

How to write rspec with mocking

I want to write an rspec test by mocking this method. Should I break this method up, as it doing multiple things?
require 'yaml'
require_relative 'checkerror'
class Operations
    def initialize
        #check
  end
    def result (result_log: File.new('result.txt', 'a+'))
      if #check.errors.empty?
 result_log.write("#{#check.checker.file_path} :: No offensenses detected\n")
#checker is instance of CheckError class
  puts "#{#check.checker.file_path} :: No offensenses detected\n"
      else
        #check.errors.uniq.each do |err|  puts "#{#check.checker.file_path} : #{err}\n"
 result_log.write("#{#check.checker.file_path} : #{err}\n")
 end
end
result_log.close
end
  end
end
If #check.errors need to be stuubed with a value and check the execution block.
It's going to be awkward mocking the f object in your current implementation, due to this line:
f = File.new('result.txt', 'a+')
You'd need to write something weird in the rspec test, like:
allow(File).to receive(:new).with('result.txt', 'a+').and_return(mock_file)
So instead, I'd recommend using dependency injection to pass the file into the method. For example:
def check_result(results_log: File.new('result.txt', 'a+'))
if #errors.empty?
# ...
end
Now, your rspec test can look something like this:
let(:results_log) { Tempfile.new }
it "prints errors to log file" do
wharever_this_object_is_called.check_result(result_log: results_log)
expect(result_log.read).to eq("checker_file_path.txt :: No offences detected\n")
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 test helper with params

I tried to test a method in helper with params[:a]. I'm not using Rspec so don't know how to solve it.
In helper file:
def get_commands(filter)
order_by = params[:order_by]
something else(filter)
end
and the test file:
test 'get_commands works' do
filter = something
res = get_commands(filter)
end
It shows: NameError: undefined local variable or method `params'
and it also doesn't work if I just add
params[:order_by]='desc'
A pretty generic approach is to use dependency injection.
First, change the method to accept params as an argument:
def get_commands(filter, params)
order_by = params[:order_by]
something else(filter)
end
Make sure to include this new argument in the controller when you call the method.
Then you can pass a mock parameter set in your test:
test 'get_commands works' do
filter = something
mock_params = ActionController::Parameters.new(order_by: :id)
res = get_commands(filter, mock_params)
# ... make your expectation about the result.
end
As a caveat, dependency injection is sometimes treated like an antipattern. Rails does have some built in helpers for testing controllers, see https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers. But using dependency injection like this would definitely work, and is a bit simpler.

how do I use rspec to test instance methods in 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

Rspec stubbing method for only specific arguments

Is there a way to stub method for only specific arguments. Something like this
boss.stub(:fire!).with(employee1).and_return(true)
If any other employee is passed to boss.fire! method, I'll get boss received unexpected message error, but what I would really like is just to override the method for specific argument, and leave it be for all others.
Any ideas how this can be done?
You can add a default stub for the fire! method which will call original implementation:
boss.stub(:fire!).and_call_original
boss.stub(:fire!).with(employee1).and_return(true)
Rspec 3 Syntax (#pk-nb)
allow(boss).to receive(:fire!).and_call_original
allow(boss).to receive(:fire!).with(employee1).and_return(true)
You can try write your own stubbing method, with code like this
fire_method = boss.method(:fire!)
boss.stub!(:fire!) do |employee|
if employee == employee1
true
else
fire_method.call(*args)
end
end

Resources