RSpec - mock a private class method call inside a request spec? - ruby-on-rails

This is my class
class MyClass
def run
to_be_mocked("arg")
## etc
end
private
def to_be_mocked(arg)
# implementation
end
end
and my Controller, which is what I am writing the request specs for, call this class.
This are my request specs:
context "Some context" do
context "some sub context" do
before :each do
allow(MyClass). to receive(: to_be_mocked).with(account.url).and_return(false)
end
it "responds with a 200" do
do_request
expect(JSON.parse(response.body)["field"]).to eq true
expect(response.status).to eq 200
end
end
However my mocking fails with an MyClass does not implement: to_be_mocked
Already tried removing the private keyword, but got the same results.
What am I missing here?

You're mocking on the class, which is how you mock you "static" class-level methods. For example, if your method was def self.foo and you called it via MyClass.foo, then allow(MyClass) is the way to go.
Your method is not a class-level method, it's an instance method. You invoke it by first creating an instead of MyClass and then calling the method on that instance. You need to use allow_any_instance_of to mock the method for all future instances of the class:
allow_any_instance_of(MyClass).to receive(....)

Related

How to test non static method was being called by other method using Rspec

I'm trying to test a method being called by another method.
I don't want to test what the other method do, because this is a separate unit test.
so let's say I have something like:
class MyService
def method_a
a = 1
b = method_b
return a + b
end
def method_b
return 2
end
end
Now, I want to test method_a - I want to verify that method_b was executed.
I know that this should work if the methods were static. But in my case, it's not static.
allow(MyService).to receive(:method_b)
I keep getting this error:
MyService does not implement method_b
And I understand that's because the method is not static, but I can't find anything in the documentation that fit my use case.
I think main problem problem is that you expecting for class method to be called and not instance
describe MyService do
it "should call method_b" do
expect(subject).to receive(:method_b).and_return(2)
subject.method_a
end
end
# P.S. it's the same as:
describe MyService do
it "should call method_b" do
service = MyService.new # instead of MyService.new you can also write described_class.new
expect(service).to receive(:method_b).and_return(2)
service.method_a
end
end

Stub unimplemented method in rspec

I'm testing my module and I decided to test it versus anonymous class:
subject(:klass) { Class.new { include MyModule } }
MyModule uses method name inside klass. To let my specs work I need to stub this method name (which is unimplemented). So I wrote:
subject { klass.new }
allow(subject).to receive(:name).and_return('SOreadytohelp') }
but it raises:
RSpec::Mocks::MockExpectationError: #<#<Class:0x007feb67a17750>:0x007feb67c7adf8> does not implement: name
from spec-support-3.3.0/lib/rspec/support.rb:86:in `block in <module:Support>'
how to stub this method without defining it?
RSpec raises this exception because it is not useful to stub a method that does not exist on the original object.
Mocking methods is always error-prone because the mock might behave differently than the original implementation and therefore specs might be successful even if the original implementation would have returned an error (or does not even exist). Allowing to mock non-existing methods is just plain wrong.
Therefore I would argue that you should not try to bypass this exception. Just add a name method to your class that raises a clear exception if run outside of the test environment:
def self.name
raise NoMethodError # TODO: check specs...
end
subject(:klass) do
Struct.new(:name) do
include MyModule
end
end
http://ruby-doc.org/core-2.2.0/Struct.html
I think that if the test you're writing is focused on your MyModule module, and that module relies on an instance method in the class that it is mixed into, then I think that method should be mocked out in the anonymous class that you use when testing the module. For example:
module MyModule
def call_name
# expected implementation of #name to be
# in the class this module is mixed into
name
end
end
RSpec.describe MyModule do
let(:my_module_able) do
Class.new do
include MyModule
# We don't care what the return value of this method is;
# we just need this anonymous class to respond to #name
def name
'Some Name that is not SOReadytohelp'
end
end.new
end
describe '#call_name' do
let(:name) { 'SOReadytohelp' }
before do
allow(my_module_able).to receive(:name).and_return(name)
end
it 'returns the name' do
expect(my_module_able.call_name).to eq(name)
end
end
end

Rspec private method can't access class instance variable?

I am testing my class' initialize method. It calls a private method, and for some reason that method is failing.
Class (code simplified for brevity):
class MyClass
#configs = {}
def initialize(configs)
#configs = configs
check_configs
create_client
end
private
def check_configs
if #configs['some_token'].nil?
Rails.logger.git_loader.error('log message')
raise AnError
end
end
end
The test:
describe '#initialize' do
let(:config) { my_token: '123-FakeToken' }
let(:loader) { described_class.new(config) }
context 'when initialized with a set of configs' do
it { expect(loader.instance_variable_get(:#configs)).to eq(configs)}
end
end
When I put a puts before the nil? check, the token prints out nothing, though when my rake task calls the initialize method, it prints fine.
Your example is a bit confusing due to the various spelling errors in your attempt to generalize your problem. I created the following two files, and the specs ran just fine. Might be a naming error that you're experiencing and not an actual rspec problem.
# test_spec.rb
require 'rspec'
require_relative 'my_class'
describe MyClass do
describe '#initialize' do
let(:configs) { {some_token: '123-FakeToken'} }
let(:loader) { described_class.new(configs) }
context 'when initialized with a set of configs' do
it { expect(loader.instance_variable_get(:#configs)).to eq(configs)}
end
end
end
and
# my_class.rb
class MyClass
##configs = {}
def initialize(configs)
#configs = configs
check_configs
end
private
def check_configs
if #configs[:some_token].nil?
puts "log message"
raise 'an error'
end
end
end
That said, the bigger question is what are you trying to accomplish with your tests?
Testing private variables is a smell. Ideally, in the case of config variables, they will cause an effect in your instance when set. For example MyClass.foo will behave differently based on whether some_token is set or not. Testing whether the behaviour of foo changes with a some_token present or not is a high value test that you want. Instead of a low value test of whether you wrote #configs = configs correctly.
Because you made your method check_configs in a private scope, you're unable to access it from the initialize method because the object you're initializing is looking for a method that is essentially hidden to the class. You either need to make the method public or rework your initialize method to not include private methods.
note, I came back and noticed this was not clear enough.
Initialize methods are always public, therefore they cannot include any private scoping within the method. You could call a private method anywhere else within the class except the initialize method
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Private

RSpec test that a class method calls an instance method

I'm looking to test that a class method calls a specific instance method. Is there any way to do this? This is the best I've got, but it fails.
describe '#foo' do
let(:job) { create :job }
it 'calls job.bar' do
job.should_receive(:bar)
Job.foo
end
end
I need to be sure that the right instance of job is called, not just any instance. I appreciate any help.
You can use stubs on the method by which .foo gets the instance.
For instance:
describe '.foo' do
let(:job) { create :job }
it 'calls job.bar' do
Job.stub(:find).and_return job
job.should_receive(:bar)
Job.foo
end
end
What this does is ensures that the instance that you expect to have methods called on is the one that actually gets used by .foo.
You can add expectations or argument matchers to this, so:
Job.should_receive(:find).with(job.id).and_return(job)

spec'ing private methods

I have some private methods in my rails models. I want to spec it out seperately (use rspec).
I do something like this
class TestModelA < ModelA
def public_wrapper_method_A
private_method_A_from_ModelA
end
end
and I write specs for TestModelA#public_wrapper_method_A
I think there is good case for writing specs for private methods.
What is the best way of writing tests for these private methods.
what do you guys think ? pros/cons?
I usually set aside a context group for private methods and then setup a describes block for each method. Within the block I define a helper method to call the private method and then the it blocks use the call_xxx method to get to the private method.
context "private methods" do
describe "#some_private_method" do
def call_some_private_method
some_obj.send(:some_private_method)
end
it "should return 'something'" do
call_some_private_method.should == 'something'
end
end
end
You don't have to wrap your class to test private methods. You can use send instead.
object.send(:foo_private_method)

Resources