Disambiguate Function calls in Ruby - ruby

I am working through Learn Ruby The Hard Way and came across something intriguing in exercise 49.
In parser.rb I have a function named skip(word_list, word_type) at the top level, which is used to skip through unrequited words (such as stop words) in user input. It is not encapsulated in a class or module. As per the exercise I have to write a unit test for the parser.
This is my code for the Unit Tests:
require "./lib/ex48/parser"
require "minitest/autorun"
class TestGame < Minitest::Test
def test_skip()
word_list = [['stop', 'from'], ['stop', 'the'], ['noun', 'west']]
assert_equal(skip(word_list, 'stop'), nil)
assert_equal(skip([['noun', 'bear'], ['verb', 'eat'], ['noun', 'honey']], 'noun'), nil)
end
end
However, when I run rake test TESTOPTS="-v" from the command line, these particular tests are skipped. This seems to be because there is a clash with the skip method in the Minitest module because they run perfectly after I change the name to skip_words.
Can someone please explain what is going on here exactly?

"Top level functions" are actually methods too, in particular they are private instance methods on Object (there's some funkiness around the main object but that's not important here)
However minitest's Test class also has a skip method and since the individual tests are instance methods on a subclass of Test you end up calling that skip instead.
There's not a very simple way of dealing with this - unlike some languages there is no easy way of saying that you want to call a particular superclass' implementation of something
Other than renaming your method, you'll have to pick an alternative way of calling it eg:
Object.new.send(:skip, list, type)
Object.instance_method(:skip).bind(self).call(list, type)
Of course you can wrap this in a helper method for your test or even redefine skip for this particular Test subclass (although that might lead to some head scratching the day someone tries to call minitest's skip.

Related

Rspec Double leaking to another example

I am testing a class that makes use of a client that makes external requests and I would like to mock this client, but verify that it gets called, however I am getting a double error.
My test looks like something like this:
describe '#execute' do
let(:attributes) { {foo: 'bar'} }
let(:client_double) { double('client', create: nil) }
let(:use_case) { described.class.new }
before do
allow(Client::Base).to receive(:new).and_return(client_double)
use_case.execute(attributes)
end
it 'creates something' do
expect(Something.find_by(foo: 'bar')).not_to be_nil
end
it 'calls client' do
expect(client).to have_received(:create).with('bar')
end
end
and the first example passes as expected, however rspec keeps breaking in the second example giving me this error:
#<Double "foo"> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
someone knows what I can do to fix it?
Reusing Fixtures with Let Methods
In this case, before is actually before(:each), which is reusing the client_double and attributes you defined with the #let helper method. The let commands make those variables functionally equivalent to instance variables within the scope of the described object, so you aren't really testing freshly-created objects in each example.
Some alternatives include:
Refactor to place all your setup into before(:each) without the let statements.
Make your tests DAMP by doing more setup within each example.
Set up new scope for a new #describe, so your test doubles/values aren't being reused.
Use your :before, :after, or :around blocks to reset state between tests, if needed.
Since you don't show the actual class or real code under test, it's hard to offer specific insights into the right way to test the object you're trying to test. It's not even clear why you feel you need to test the collaborator object within a unit test, so you might want to give some thought to that as well.
It turns out I was using a singleton as a client and haven't realized before, so it was trully class caching it through examples. To fix it all I did was mock the instantiate method instead of the new method and everything worked.
So in the end this worked:
allow(Client::Base).to receive(:instantiate).and_return(client_double)

How do I test a function's logic but ignore the method calls?

I'm writing a Rspec test for existing code that I've added some logic to that I want to ensure is working properly. I do not care about the returned values, just that the logic works on an object that I pass to it. For simplicity here is an example of very a basic function from within a class I have built:
def function_I_want_to_test(thing)
if thing > 0
function_a(thing)
else
function_b(thing)
end
end
function_a and function_b already have spec tests, so I don't really care what the return values are, but both function_a and function_b make API calls which I feel would make the rspec test a lot more complicated for testing only the logic contained within function_I_want_to_test.
I've gone through this page (https://blog.codeship.com/unit-testing-in-ruby/) reviewing the unit testing approach, and the explanations make sense, but when attempting to use allow and receive, it appears that the function is still wanting to be called so I'm assuming I'm not utilizing the functions correctly.
Essentially I want to treat my code as such, and have it return something arbitrary so it doesn't even call the function.
def function_I_want_to_test(thing)
if thing > 0
pp "a"
else
pp "b"
end
end
From my research, it appears the allow/expect is the way to go, but I'm assuming I'm not fully understanding the concept.
I guess the main question I have is, how does my rspec test know to "override" the functions (function_a and function_b) when testing function_I_want_to_test?
I know there are a few similar asked questions, but they didn't seem to answer how I was anticipating.

How to prevent classes to be overridden in Ruby?

I'm setting up rules how to write minitests, and I want to prevent other programmers to override methods for existing classes, because it will affect other tests, where this class will be used. Here is some clarifications below.
For example, I have a class in the lib folder, KlassExample. It has a public method do_something with own logic. In minitest someone could want to override this method with other logic. I want to not allow run test, if the class logic was overridden.
Code samples:
lib/klass_example.rb
class KlassExample
def do_something
false
end
end
test/unit/lib/klass_example_test.rb
require 'unit/test_helper'
require 'klass_example'
class KlassExample
def do_something
true
end
end
class KlassExampleTest < Minitest::Test
def test_do_something
assert_equal true, KlassExample.new.do_something
end
end
I want that programmers would use MiniTest::Mock instead of class overriding, so I need some coercive actions to get them to write code in the right way.
Is there any possible complex solution how to do it?
While this particular use case might be somewhat covered with TracePoint#new(:class) by setting a TracePoint on the class re-opening and e.g. raising from there, you would never prevent all the possibilities explicitly and implicitly built into Ruby to indeed allow developers to do whatever they want.
Overwritten Module#prepended callback would disallow others to prepend modules to your class:
KlassExample.prepend(Module.new { def self.prepended(*); raise end })
There are ways to prevent Module#define_method calls, also they are already looking like hacks.
But the whole set of possibilities to fool your guard in nearly infinite, so I doubt it’s doable in general. I bet every time you would think “ok, now everything is covered,” I’d easily invent another cumbersome way to bypass all your guards.
Ruby is not the language designed to prevent developers from doing whatever they want.

In a Ruby module, how do you test if a method exists in the context which use the module?

Some context
I'm playing with Ruby to deepen my knowledge and have fun while at the same time improving my knowledge of Esperanto with a just starting toy project called Ĝue. Basically, the aim is to use Ruby facilities to implement a DSL that matches Esperanto traits that I think interesting in the context of a programming language.
The actual problem
So a first trait I would like to implement is inflection of verbs, using infinitive in method declaration (ending with -i), and jussive (ending with -u) for call to the method.
A first working basic implementation is like that:
module Ĝue
def method_missing(igo, *args, &block)
case igo
when /u$/
celo = igo.to_s.sub(/u$/, 'i').to_s
send(celo)
else
super
end
end
end
And it works. Now the next step is to make it more resilient, because there is no guaranty that celo will exists when the module try to call it. That is, the module should implement the respond_to? method. Thus the question, how do the module know if the context where module was required include the corresponding infinitive method? Even after adding extend self at the beginning of the module, inside of the module methods.include? :testi still return false when tested with the following code, although the testu call works perfectly:
#!/usr/bin/env ruby
require './teke/ĝue.rb'
include Ĝue
def testi; puts 'testo!' ;end
testu
Note that the test is run directly into the main scope. I don't know if this makes any difference with using a dedicated class scope, I would guess that no, as to the best of my knowledge everything is an object in Ruby.
Found a working solution through In Ruby, how do I check if method "foo=()" is defined?
So in this case, this would be checkable through
eval("defined? #{celo}") == 'method'

Mocking constructors in Ruby

I'm a Java-developer toying with Ruby, and loving it. I have understood that because of Ruby's metaprogramming facilities my unit-tests become much cleaner and I don't need nasty mocking frameworks. I have a class which needs the File class's services and in my test I don't want to touch my real filesystem. In Java I would use some virtual file system for easier "seams" to pass fake-objects in but in Ruby that's obviously overkill. What I come up seems already really nice compared to the Java-world. In my class under test I have an optional constructor parameter:
def initialize(file_class=File)
When I need to open files within my class, I can then do this:
#file_class.open(filename)
And the call goes to either the real File-class, or in case of my unit-test, it goes to a fake-class which doesn't touch the filesystem. I know there must be a better way to do this with metaprogramming?
Mocha (http://mocha.rubyforge.org/) is a very good mocking library for ruby. Depending on what you're actually wanting to test (i.e. if you want to just fake out the File.new call to avoid the file system dependency or if you want to verify that the correct arguments are passed into File.new) you could do something like this:
require 'mocha'
mock_file_obj = mock("My Mock File") do
stubs(:some_instance_method).returns("foo")
end
File.stubs(:new).with(is_a(String)).returns(mock_file_obj)
In the case you've outlined, I'd suggest that what you're doing seems fine. I know that it's a technique that James Mead (the author of Mocha) has advocated. There's no need to do metaprogramming just for the sake of it. Here's what James has to say about it (and a long list of other techniques you could try)
This is a particularly difficult challenge for me. With the help I received on this question, and some extra work on my behalf, here's the solution I arrived at.
# lib/real_thing.rb
class RealThing
def initialize a, b, c
# ...
end
end
# test/test_real_thing.rb
class TestRealThing < MiniTest::Unit::TestCase
class Fake < RealThing; end
def test_real_thing_initializer
fake = mock()
Fake.expects(:new).with(1, 2, 3).returns(fake)
assert_equal fake, Fake.new(1, 2, 3)
end
end

Resources