Stub a function which is inside the function to be tested - ruby

I have a function let's say A whose output and functionality I have to test, A calls another function B which takes a lot of time to compute the output. So I am trying to use stubs to mimic all the values that B returns.
def A
#do something
output = B
#do something with output
end
Now the test files
describe "check what A returns" do
ClassName.stub(:B) do
[0, 1]
end
test_values = TestClass.A(input parameters)
#checks on test values
end
My aim is to pass the expected output of B to function A. I am using RSpec. How do I go about it?

With RSpec you can do:
allow(ClassName).to receive(:B).and_return([1,2,3,4,5])
After this you can call B function and it will return [1,2,3,4,5]
You can find more info at RSpec documentation: https://relishapp.com/rspec/rspec-mocks/v/3-4/docs/configuring-responses/returning-a-value

I've attempted to write some classes and test cases for what it seems like you want to test. The key here is to use allow to stub out the return value for a method.
Just note here that I've changed the methods in your class to be class methods to fit what seems to be your test case, but you can obviously change them back to instance methods to fit your purpose. Also, accepted Ruby style is to have lowercase method names.
class ClassName
def self.B
# returns something that we're going to stub out
end
end
class TestClass
def self.A
# do something
output = ClassName.B
# do something with output
# eg in this case, add a value to it
output << 2
end
end
describe TestClass do
describe '.A' do
before do
allow(ClassName).to receive(:B).and_return([0, 1])
end
it 'does something with output' do
expect(described_class.A).to eq([0, 1, 2])
end
end
end

There's ways as mentioned in other posts but I'll give you another: you might want to make that dependency explicit.
Here's how it could look like:
# test_class.rb
class TestClass
# The default will be automatically setup to be an object of type ClassName
def initialize(some_collaborator: ClassName.new)
self.some_collaborator = some_collaborator # Some people will probably also insert some guard (to make sure it responds to `b`
end
def a
# your code calling `some_collaborator.b`
end
private
attr_accessor :some_collaborator
end
# test_class_spec.rb
describe TestClass do
let(:stub_b) { stub("Some instance of ClassName", b: [...] }
subject { TestClass.new(some_collaborator: stub_b) }
it "whatever" do
expect(subject.a).to ...
end
end
The default collaborator should be a sensible default (and if you can't instantiate it there's ways to encapsulate it anyways). Not only it will be easier to read, but it will be easier to maintain.

Related

How do you check the return value of a method called inside another method rspec?

Let's say you have some class like
class Foo
...
public def methodA
x = methodB(true)
# other operations (assume x is not the return value of methodA)
end
private def methodB(arg)
if arg
return 1
else
return 0
end
end
end
When you're writing a unit test for methodA, you want to check that x was assigned the right value, which means you have to check that the call to methodB returned 1 like you expected.
How would you test that in rspec?
Right now I have (with help from How to test if method is called in RSpec but do not override the return value)
#foo = Foo.new
expect(#foo).to receive(:methodB).with(true).and_call_original
But I don't know how to verify the actual return value of methodB.
I think instead of mocking you can just call the private method?
#foo = Foo.new
result = foo.send(:methodB, true)
expect(result).to eq 1
This is basically testing the private method directly. Some people frown upon doing that but if it has lots of logic it's sometimes easier to test it directly. I agree with #spickermann that it's usually best to test the public method and leave the implementation details of the private methods out of the specs.
Don't Test Multiple Methods in a Single Unit Test
You're going about this wrong, because you're creating an unnecessary dependency between two different methods. Instead, you should refactor your code so that:
x is an argument to #methodA,
x, #x, or ##x is a variable accessible to methodA that you can set to whatever expected value you want, or
stub out #methodB for this unit test.
As an example of the last:
describe Foo do
describe 'methodA' do
it 'does something when methodB returns 1' do
# stub the default subject, which is Foo.new
allow(subject).to receive(:methodB) { 1 }
expect(subject.methodA).to eq('foo bar baz')
end
end
end
This code is untested, because your posted code is not a complete, verifiable example. While you'd really be better off refactoring your code so you aren't testing nested depedencies at all, using test doubles or method stubs are certainly viable options.

caller_method returns not the value, that i expected

I want to know, what method calls another method (I'm just trying to create simple expect("string").to eq("string") model (just like in RSpect, but more easier).
But i get "main", what is that? (I see that "main" for first time)
public
def expect(message)
message.to_s
end
def to
caller_method = caller_locations.first.label
puts caller_method
end
expect("test").to #=> <main>
#what output i expected:
expect("test").to #=> expect
My goal:
#first i need to do something like that:
expect("test").to eq("test") #=> true
#final must look like this:
expect(expect("test").to eq("test")).to eq(true) #=> true
I would recommend against using caller_method in this case. Rather, make a class whose methods return self - that way they will be chainable:
module Expectation
attr_accessor :caller_method
def expect(arg)
self.caller_method = "expect"
self
end
def to
caller_method
end
end
include Expectation
expect("foo").to
# => "expect"
Obviously this is only a starting point, and this doesn't actually do any comparisons / validations yet. But hopefully you can understand this pattern. The key thing is returning self to make a chainable API, and storing internal state using something like attr_accessor

Make Ruby object respond to double splat operator **

I have a library that has an #execute method like this
def execute(query, **args)
# ...
end
I have a class that generates the data for args (which has a lot of logic depending on user abilities)
class Abilities
def to_h
{ user: user } # and a lot more data
end
end
Now when I'm using #execute I always have to remember to use #to_h, which is pretty annoying and leads to mistakes when someone forgets it:
execute(query, abilities.to_h)
So I was wondering if my Abilities class could somehow respond to the ** (double splat) operator, so that I can simply pass the object:
execute(query, abilities)
When I try to call it like this, it throws an error:
ArgumentError: wrong number of arguments (given 2, expected 1)
So, is there any way to make my Abilities class behave like a Hash? I could derive it like this Abilities < Hash but then I have all the Hash logic on it, which seems pretty dirty.
You can implement to_hash: (or define it as an alias for to_h)
class MyClass
def to_hash
{ a: 1, b: 2 }
end
end
def foo(**kwargs)
p kwargs: kwargs
end
foo(MyClass.new)
#=> {:kwargs=>{:a=>1, :b=>2}}
If you specify the API to execute in such a way that it accepts anything that supports to_h, then you have a solution:
def execute(query, args = {})
args = args.to_h
...
end

How to compactly write a block that executes a method with arguments

In the following code:
def main
someArray.all? { |item| checkSomething(item) }
end
private
def checkSomething(arg)
...
end
How do I shorten the all? statement in order to ged rid of the redundant item variable?
I'm looking for something like someArray.all?(checkSomething) which gives a "wrong number of arguments" error.
You could have a slightly shorter code if checkSomething was a method on your object class. Don't know what it is, so, I'm guessing, you're working with primitives (numbers, strings, etc.). So something like this should work:
class Object
def check_something
# check self
end
end
some_array.all?(&:check_something)
But this is, of course, a horrible, horrible way of going about it. Saving a few keystrokes at the cost of such global pollution - absolutely not worth it. Moreover, even this trick will not be available as soon as you will need to pass additional parameters to the check method.
Besides, the original code is quite readable too.
You could use Object#method and Method#to_proc (i.e. &method) to get rid of the item variable, although it is slower:
def main(array)
array.all?(&method(:check_something))
end
def check_something(arg)
arg.odd?
end
main [1,3,5] #=> true
main [1,3,6] #=> false
If checkSomething is an item method (i.e. defined in the class of the 'i' object) you could do symbol to proc...
def main
someArray.all?(&:checkSomething)
end
A method only has access to passed arguments, or to selfso to bypass passing arguments you need to make the method an instance method of the object class (so it can use self)
The way you have it... where checkSomething is external to the i class... you can't do that.
Considering you want to keep your object's checkSomething private, I think this would be a good work around :
class Something
def main
someArray.all?(&checkSomething)
end
private
def checkSomething
->(item) do
# Checking part.
end
end
end
For block that executes a method with arguments, Checkout this way...
def main
someArray.all? &checkSomething(arg1, arg2, ...)
end
private
def checkSomething(arg1, arg2, ...)
Proc.new { |item| ..... }
end
could you not use a Ruby's collection method 'any?' instead?
def main
#students is an array of students
students.any?(&:passed)
end
class Student
def passed
#code to check if student passed
end
end
Ref http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-any-3F

Ruby: Passing a block to a class macro that defines instance methods

I'm struggling with code that looks like the example below (but actually does something useful). The block that is passed to def_my_method is of course created in the context of the class, when I want to evaluate it in the context of the instance that has the instance method. How do I do this?
module Formatters
# Really useful methods, included in several classes
def format_with_stars(str)
return "*** #{str} ***"
end
end
class Test
include Formatters
STRINGS = ["aa", "bb"]
def self.def_my_method(method_name, another_parameter, &format_proc)
define_method(method_name) do
# In reality, some more complex code here, then...
return STRINGS.map(&format_proc)
end
end
def_my_method(:star_strings, "another_parameter") { |str| format_with_stars(str) }
# Define other methods
end
tt = Test.new
puts tt.star_strings
# Throws undefined method `format_with_stars' for Test:Class (NoMethodError)
You can use instance_exec to execute the passed block in the right context. Instead of passing &format_proc directly to the call to map, pass a block that calls it using instance exec.
Something like this:
def self.def_my_method(method_name, another_parameter, &format_proc)
define_method(method_name) do
# In reality, some more complex code here, then...
return STRINGS.map{|str| instance_exec(str, &format_proc)}
end
end
This produces this:
$ ruby tt.rb
*** aa ***
*** bb ***
for me (where tt.rb is the arbitary name I gave the file), which I think is what you want for this example.
...
class Test
- include Formatters
+ extend Formatters
...
should do the trick.

Resources