How to skip the method of a callback in rspec when there is no method name? - activerecord

I have some callbacks like this:
after_destroy :send_email
And I can skip them in Rspec passing the name:
Model.skip_callback(:destroy, :after, :send_email)
But what if I have something like:
after_destroy { |instance| do_something(instance) }
How can I skip this?

You can try doing before your test following line
ActiveRecord::Base.skip_callbacks = true
Now write your test and then enable callbacks by doing following.
ActiveRecord::Base.skip_callbacks = false
But I think it is not good thing to skip_callbacks.

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 a method is called with Minitest?

class Post
def save
Mailer.notify!('bla')
true
end
end
How to test that when post.save is called, the Mailer.notify method is fired? and with the right arguments.
With RSpec I usually do:
expect(Mailer).to receive(:notify!).with('bla')
post.save
Thank you in advance!
You can do something like this:
describe 'Post#save' do
it "calls Mailer::notify!" do
mock = MiniTest::Mock.new
mock.expect(:call, nil, ['bla'])
Mailer.stub(:notify!, mock) do
post.save
end
mock.verify
end
end
And yes, that is easier and more intuitive in RSpec...
For mocking with minitest I like using Mocha gem https://github.com/freerange/mocha
I will be:
Mailer.expects(:notify!).with('bla').at_least_once

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.

rails rspec - how to check for a model constant?

How can I do something like:
it { should have_constant(:FIXED_LIST) }
In my model (active record) I have FIXED_LIST = 'A String'
It's not a db attribute or a method and I haven't been able to use responds_to or has_attribute to test for it (they fail). What can I use the to check for it. - btw I have the shoulda-matchers installed.
Based on David Chelimsky's answer I've got this to work by slightly modifying his code.
In a file spec/support/utilities.rb (or some other in spec/support) you can put:
RSpec::Matchers.define :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
Note the use of "RSpec::Matchers.define" in stead of "matchers"
This allows to test for constants in your specs, like:
it "should have a fixed list constant" do
YourModel.should have_constant(:FIXED_LIST)
end
Note the use of "have_constant" in stead of "have_const"
It reads a little silly, but:
describe MyClass do
it { should be_const_defined(:VERSION) }
end
The reason is that Rspec has "magic" matchers for methods starting with be_ and have_. For example, it { should have_green_pants } would assert that the has_green_pants? method on the subject returns true.
In the same fashion, an example such as it { should be_happy } would assert that the happy? method on the subject returns true.
So, the example it { should be_const_defined(:VERSION) } asserts that const_defined?(:VERSION) returns true.
If you want to say have_constant you can define a custom matcher for it:
matcher :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
MyClass.should have_const(:CONST)
If you're trying to use the one-liner syntax, you'll need to make sure the subject is a class (not an instance) or check for it in the matcher:
matcher :have_constant do |const|
match do |owner|
(owner.is_a?(Class) ? owner : owner.class).const_defined?(const)
end
end
See http://rubydoc.info/gems/rspec-expectations/RSpec/Matchers for more info on custom matchers.
HTH,
David
Another option to simply make sure the constant is defined – not worrying about what it's defined with:
it 'has a WHATEVER constant' do
expect(SomeClass::WHATEVER).not_to be_nil
end
A warning to anyone trying to test that constants are defined: If your code references an undefined constant while defining a class, then your specs will crash before they get to your test.
This can lead you to believe that
expect { FOO }.to_not raise_error
is failing to catch the NameError, because you'll get a big stack trace, instead of a nice "expected not to raise error, but raised NameError."
Amidst the huge stack trace, it can be difficult to notice that your test is actually crashing on line 1: requre "spec/spec_helper" because your entire application is failing to load before it gets to your actual test.
This can happen if you have dynamically defined constants, such as is done by ActiveHash::Enum, and you then use them in the definition of another constant. Don't bother testing that they exist, every spec in your app will crash if one of them fails to be defined.
You could use
defined? YOUR_MODEL::FIXED_LIST
In RSpec 2, I was able to get this to work in one line as follows:
it { subject.class.should be_const_defined(:MY_CONST) }
That is, check against the class, instead of the instance.
In My model
class Role < ActiveRecord::Base
ROLE_ADMIN = "Administrador"
end
In My rspec
RSpec.describe Role, type: :model do
let(:fake_class) { Class.new }
describe "set constants" do
before { stub_const("#{described_class}", fake_class) }
it { expect(described_class::ROLE_ADMIN).to eq("Administrador") }
end
end
For ruby 2.1.5 and rspec 3.5.0 I am able to test that constant SEARCH_CHARS_TO_IGNORE is defined in the class DiffAlertsDatatable as follows:
expect(DiffAlertsDatatable.const_defined?(:SEARCH_CHARS_TO_IGNORE)).to eq(true)

Resources