Testing infinite loop with MiniTest and using mocking - ruby

I have a section in my code which polls a queue for a message and then acts on it depending on the message type:
#queue = Foo::Queue.new
loop do
#queue.poll do |message|
if message[:task] == TAKEACTION
result = takeaction(message)
#queue.send_result(result, message)
end
#queue.pong(message) if message[:task] == PING
end
end
How do I set up a test to supply a single message and verify that the #queue acts on it as I expect?
I have seen very little about testing blocks in minitest, and haven't found anything in ruby regarding breaking out of infinite loops, though I found one idea in python where you set up the second run to throw an exception.
Can any ruby / minitest gurus help?

For minitest using a stub will work. The example below is self contained and can run on its own. You can send an exception as a lambda with a stub to break the infinite loop and continue testing.
# class receiving the messages from class being tested
class Obj
def method_if_true
# do some stuff
return 'anything that is true'
end
def method_if_false
# do some stuff
false
end
end
# class to be tested
class TestingLoop
def initialize(args)
#obj = args.fetch(:object, Obj.new)
#bool = args[:bool]
end
def looping
loop do
if #bool
#obj.method_if_true
# #bool is true
elsif !#bool
#obj.method_if_false
# #bool is false
end
end
end
end
require 'minitest/autorun'
# class that tests
class TestTestingLoop < MiniTest::Test
def setup
#obj = Obj.new
#raises_exception = lambda { raise RuntimeError.new }
# could use the '->' syntax instead of the word lambda
end
def test_sends_correct_method_when_true
#obj.stub :method_if_true, #raises_exception do
testing_loop = TestingLoop.new({object: #obj, bool: true})
assert_raises(RuntimeError) { testing_loop.looping }
end
end
def test_sends_correct_method_when_false
#obj.stub :method_if_false, #raises_exception do
testing_loop = TestingLoop.new({object: #obj, bool: false})
assert_raises(RuntimeError) { testing_loop.looping }
end
end
end

Related

Delegating everything but allowing the possibility to pre-extend our proxy classes

Given a set of class definitions:
class Application
def self.open_current
return Current.new()
end
end
class Current
def get_row(row)
Row.new(row)
end
end
class Row
def get_col(row)
#...
end
end
Design a Proxy class which will:
Create Proxy<<Class>> versions of each class which are extendable via
class ProxyApplication
def myMethod()
#...
end
end
#...
Wrap all return values of all methods in each class such that a proxied class is always used instead of a standard class. I.E.
app = Proxy.new(Application) #app == ProxyApplication
current = app.open_current #current == #<ProxyCurrent>
Ultimately, the definition of Proxy must be dynamic rather than static definitions.
I've been working on this problem for about 6 hours now. I've got the following code. This includes 3 sections:
Initial class setup
Proxy class definition
Testing proxy class
Currently I've got to the point where pApplication=Proxy.new(Application) returns #<Proxy> and pApplication.open_current returns #<ProxyCurrent> which seems kind of on the correct line. However currently it errors when delegate.rb tries to call test() with 2,3 arguments instead of 0...
But my question is, realistically am I going about this correctly? Is using SimpleDelegator the easiest way to do this? One current problem is I'm basically having to add new functionality to the existing SimpleDelegator. I've also looked at using Forwardable, but having to delegate methods manually is not where I want to go with this project, if possible.
Any ideas?
Due to numerous limitations with the initial idea of packing all the calls into a single class, I redesigned it a bit, but it works in exactly the same way.
def proxy__enwrap(obj)
isClass = obj.is_a?(Class)
oldClass = isClass ? obj : obj.class
sNewClass = "Proxy#{oldClass.to_s}"
code = <<-EOF
class #{sNewClass}
include InstanceProxy
def self.__cinit__(obj)
##__cobj__ = obj
end
def self.__cget__
##__cobj__
end
def self.method_missing(m,*args,&block)
if ##__cobj__.respond_to? m
retVal = ##__cobj__.public_send(m,*args,*block)
return proxy__enwrap(retVal)
else
puts "ERROR " + m.to_s + "(" + args.to_s + ") + block?"
#Throw error
end
end
end
#{sNewClass}.__cinit__(#{oldClass.to_s})
if isClass
return #{sNewClass}
else
return #{sNewClass}.new(obj)
end
EOF
::Kernel.eval(code)
end
module InstanceProxy
def method_missing(m,*args,&block)
retVal = #__obj__.__send__(m,*args,&block)
return proxy__enwrap(retVal)
end
def initialize(obj)
#__obj__ = obj
end
end
XXApplication = Application
::Object.const_set "Application", proxy__enwrap(Application)
Currently the only issue is that the object doesn't wrap correctly around yielded objects... I'm not sure if that's even possible though.
Edit:
I've improved the system to also wrap objects passed in as blocks:
def proxy__enwrap(obj)
isClass = obj.is_a?(Class)
oldClass = isClass ? obj : obj.class
sNewClass = "Proxy#{oldClass.to_s}"
code = <<-EOF
class #{sNewClass}
include InstanceProxy
def self.__cinit__(obj)
##__cobj__ = obj
end
def self.__cget__
##__cobj__
end
def self.to_ary
#fix for puts (puts calls to_ary. see: https://stackoverflow.com/questions/8960685/ruby-why-does-puts-call-to-ary)
[self.to_s]
end
def self.method_missing(m,*args,&block)
#Wrap block arguments:
newBlock = Proc.new {}
if block_given?
newBlock = Proc.new do |*args|
args = args.map {|arg| proxy__enwrap(arg)}
block.call(*args)
end
end
#Call delegated functions. Raise error if object doesn't respond to method.
#Return wrapped value
if ##__cobj__.respond_to? m
retVal = ##__cobj__.public_send(m,*args,*block)
return proxy__enwrap(retVal)
else
raise ArgumentError.new("Method '\#\{m.to_s}' doesn't exist.")
end
end
end
#{sNewClass}.__cinit__(#{oldClass.to_s})
if isClass
return #{sNewClass}
else
return #{sNewClass}.new(obj)
end
EOF
::Kernel.eval(code)
end
module InstanceProxy
def method_missing(m,*args,&block)
#Wrap block arguments:
newBlock = Proc.new {}
if block_given?
newBlock = Proc.new do |*args|
args = args.map {|arg| proxy__enwrap(arg)}
block.call(*args)
end
end
#Call delegated functions. Raise error if object doesn't respond to method.
#Return wrapped value
if #__obj__.respond_to? m
retVal = #__obj__.__send__(m,*args,&newBlock)
return proxy__enwrap(retVal)
else
raise ArgumentError.new("Method '#{m.to_s}' doesn't exist.")
end
end
def initialize(obj)
#__obj__ = obj
end
def to_ary
#fix for puts (puts calls to_ary. see: https://stackoverflow.com/questions/8960685/ruby-why-does-puts-call-to-ary)
[self.to_s]
end
end
#
XXApplication = Application
#Silence warnings of overwriting constant
original_verbosity = $VERBOSE
$VERBOSE = nil
::Object.const_set "Application", proxy__enwrap(Application)
$VERBOSE = original_verbosity

Ruby assignment methods won't receive a block?

I am building a DSL and have this module
module EDAApiBuilder
module Client
attr_accessor :api_client, :endpoint, :url
def api_client(api_name)
#apis ||= {}
raise ArgumentError.new('API name already exists.') if #apis.has_key?(api_name)
#api_client = api_name
#apis[#api_client] = {}
yield(self) if block_given?
end
def fetch_client(api_name)
#apis[api_name]
end
def endpoint(endpoint_name)
raise ArgumentError.new("Endpoint #{endpoint_name} already exists for #{#api_client} API client.") if fetch_client(#api_client).has_key?(endpoint_name)
#endpoint = endpoint_name
#apis[#api_client][#endpoint] = {}
yield(self) if block_given?
end
def url=(endpoint_url)
fetch_client(#api_client)[#endpoint]['url'] = endpoint_url
end
end
end
so that I have tests like
context 'errors' do
it 'raises an ArgumentError when trying to create an already existent API client' do
expect {
obj = MixinTester.new
obj.api_client('google')
obj.api_client('google')
}.to raise_error(ArgumentError,'API name already exists.')
end
it 'raises an ArgumentError when trying to create a repeated endpoint for the same API client' do
expect {
obj = MixinTester.new
obj.api_client('google') do |apic|
apic.endpoint('test1')
apic.endpoint('test1')
end
}.to raise_error(ArgumentError,"Endpoint test1 already exists for google API client.")
end
end
I would rather have #api_clientwritten as an assignment block
def api_client=(api_name)
so that I could write
obj = MixinTester.new
obj.api_client = 'google' do |apic| # <=== Notice the difference here
apic.endpoint('test1')
apic.endpoint('test1')
end
because I think this notation (with assignment) is more meaningful. But then, when I run my tests this way I just get an error saying that the keyworkd_do is unexpected in this case.
It seems to me that the definition of an assignment block is syntactic sugar which won't contemplate blocks.
Is this correct? Does anyone have some information about this?
By the way: MixinTester is just a class for testing, defined in my spec/spec_helper.rb as
class MixinTester
include EDAApiBuilder::Client
end
SyntaxError
It seems to me that the definition of an assignment [method] is syntactic
sugar which won't contemplate blocks.
It seems you're right. It looks like no method with = can accept a block, even with the normal method call and no syntactic sugar :
class MixinTester
def name=(name,&block)
end
def set_name(name, &block)
end
end
obj = MixinTester.new
obj.set_name('test') do |x|
puts x
end
obj.name=('test') do |x| # <- syntax error, unexpected keyword_do, expecting end-of-input
puts x
end
Alternative
Hash parameter
An alternative could be written with a Hash :
class MixinTester
def api(params, &block)
block.call(params)
end
end
obj = MixinTester.new
obj.api client: 'google' do |apic|
puts apic
end
#=> {:client=>"google"}
You could adjust the method name and hash parameters to taste.
Parameter with block
If the block belongs to the method parameter, and not the setter method, the syntax is accepted :
def google(&block)
puts "Instantiate Google API"
block.call("custom apic object")
end
class MixinTester
attr_writer :api_client
end
obj = MixinTester.new
obj.api_client = google do |apic|
puts apic
end
# =>
# Instantiate Google API
# custom apic object
It looks weird, but it's pretty close to what you wanted to achieve.

Better way to test if method is called x times with MiniTest?

Today I've started with some basic implementation of minitest and finally figured a way out to test if a method on a class is called twice.
In RSpec I would do something like:
expect(#foo).to receive(:some_heavy_calculation).once
2.times { #foo.bar }
Now, I've came up with the following implementation for MiniTest, but I'm not sure if this is the way to implement this, because this. Here's what I've got
require 'minitest/autorun'
class Foo
def bar
#cached_value ||= some_heavy_calculation
end
def some_heavy_calculation
"result"
end
end
class FooTest < Minitest::Test
def setup
#foo = Foo.new
end
def cache_the_value_when_calling_bar_twice
mock = Minitest::Mock.new
mock.expect(:some_heavy_calculation, [])
#foo.stub :some_heavy_calculation, -> { mock.some_heavy_calculation } do
2.times { assert_equal_set #foo.bar, [] }
end
mock.verify
end
end
Do I really have to implement this with a mock, which will be the result of the stub of the subject on the method that has to be called x times?
I had to do something similar. This is what I ended up with...
def cache_the_value_when_calling_bar_twice
count = 0
#foo.stub :some_heavy_calculation, -> { count += 1 } do
2.times { assert_equal_set #foo.bar, [] }
end
assert_equal 1, count
end
I did something like this in one of my tests. This also works if your method is potentially invoking multiple instances of a class:
test "verify number of method calls" do
count = 0
Foo.stub_any_instance(:some_heavy_calculation, -> { count += 1 }) do
2.times { assert_equal_set #foo.bar, [] }
end
assert_equal 1, count
end

How do I attach an observer to a method without adding code to the method itself?

I'm trying to figure out a way to have a method trigger another method by creating a new Listener class. I'd really like the code to be simplified and not involve adding anything specific to the callback method or the trigger method. Basically, what I'm trying to do is this:
def level_up
level += 1
end
def print_level
puts "Level Up! (#{level})"
end
notify_level = Listener.new(:level_up, :print_level);
What my Listener class is (right now) is this:
# Listener.new(attached_to, callbacks)
class Listener
def initialize(attached_to, function)
#owner, #callback = attached_to, function
end
def owner
#owner
end
def callback
#callback
end
def trigger
# execute callback manually
self.method(#owner).call
self.method(#callback).call
end
end
In order to call both, I need to execute notify_level.trigger itself, but what I want is to execute level_up and call print_level. I know someone will mention something about observers, but I need more than just that. I want to hold fast to DRY. Manually adding observers and listeners to every single method is just terrible, especially since I can't add or remove them with ease.
Personally I'm not a big fan of this pattern but this is kind of a fun question so here is my solution. Should work in Ruby 1.9 and greater.
module MethodListener
##observed_methods = {}
def method_added(method)
alias_name = "__#{method}_orig"
return if method_defined?(alias_name) || method.match(/__.*_orig/)
alias_method alias_name, method
define_method(method) do |*args|
ret = send(alias_name, *args)
(##observed_methods[method] || []).each {|callback| send(callback)}
ret
end
end
def listen(owner, callback)
(##observed_methods[owner] ||= []) << callback
end
end
Usage example:
class A
extend MethodListener
def b(a,b)
puts "b #{a} #{b}"
true
end
def c
puts 'c'
end
listen :b, :c
end
A.new.b(1,2) # => true
# Prints:
# b 1 2
# c
I changed my original code to be more semantic and so it would make more sense.
class Event
def initialize(event, callback_array = [])
if callback_array.kind_of? Array
#callbacks = callback_array
else
#callbacks = [callback_array]
end
#event = event
end
def trigger(*args)
self.method(#event).call *args
#callbacks.each{ |callback|
if callback.instance_of? Event
callback.trigger *args
else
method(callback).call *args
end
}
end
def add(callback)
#callbacks.push callback
end
def remove(callback)
#callbacks.delete_at(#callbacks.index(callback) || #callbacks.length)
end
def event_name
#event
end
end
Usage:
$infinite_break = 10
def infinite_loop_a(type)
puts "#{$infinite_break} points of #{type} damage taken"
$infinite_break -= 1
if $infinite_break > 0
$infinite.trigger(type)
else
$infinite.remove(:infinite_loop_a)
end
end
def infinite_loop_b(type)
puts "player is dealing #{$infinite_break} damage"
end
$infinite = Event.new(:infinite_loop_b, :infinite_loop_a)
$infinite.trigger('fire')
Also, I know I'm calling the infinite_loop_b inside infinite_loop_a, but that's for a specific reason. The Event instances can have another Event as a callback.

How do I run code before and after a method in a sub class?

My first thoughts are some thing like this:
class AbstractBuilder
attr_reader :time_taken
def build_with_timer
started_at = Time.now
build
#time_taken = Time.now - started_at
end
def build
raise 'Implement this method in a subclass'
end
end
class MyBuilder < AbstractBuilder
def build
sleep(5)
end
end
builder = MyBuilder.new.build_with_timer
puts builder.time_taken
I would suspect there is a better way which offers better flexibility, for example ideally I'd like to call 'build' on an instance of MyBuilder instead of 'build_with_timer' and always have the execution time recorded.
I did consider using alias_method from initialize or even using a module mixin instead of class inheritance which would override the build method calling super in the middle (not sure if that would work). Before I go down the rabbit hole I thought I'd see if there is an established practice.
I had a stab at a version to achieve what you want. This version doesn't require the subclass to have any extra code either.
class AbstractBuilder
##disable_override = false
def before_method
puts "before"
end
def after_method
puts "after"
end
def self.method_added name
unless ##disable_override
if name == :build
##disable_override = true # to stop the new build method
self.send :alias_method, :sub_build, :build
self.send :remove_method, :build
self.send :define_method, :build do
before_method
sub_build
after_method
end
##disable_override = false
else
puts "defining other method #{name}"
end
end
end
end
class MyBuilder < AbstractBuilder
def build
puts "starting build"
sleep(5)
puts "built."
end
def unnaffected_method
# this method won't get redefined
end
end
b = MyBuilder.new
b.build
Outputs
defining other method unnaffected_method
before
starting build
built.
after
I'd play with alias_method:
module Timeable
def time_methods *meths
meths.each do |meth|
alias_method "old_#{meth}", meth
define_method meth do |*args|
started_at = Time.now
res = send "old_#{meth}", *args
puts "Execution took %f seconds" % (Time.now - started_at)
res
end
end
end
end
class Foo
def bar str
puts str
end
end
Foo.extend Timeable
Foo.time_methods :bar
Foo.new.bar('asd')
#=>asd
#=>Execution took 0.000050 seconds
Sounds like you're looking for hooks into object lifecycle events. You'll have to build this into your base object and provide a little DSL -- I'm thinking you're after something like ActiveRecord Callbacks. Here's how we might modify your example to allow something like that:
class AbstractBuilder
attr_reader :time_taken
def construct! # i.e., build, and also call your hooks
##prebuild.each { |sym| self.send(sym) }
build
##postbuild.each { |sym| self.send(sym) }
end
def construct_with_timer
started_at = Time.now
construct!
#time_taken = Time.now - started_at
puts "!!! Build time: ##time_taken"
end
class << self
def before_build(fn); ##prebuild ||= []; ##prebuild << fn; end
def after_build(fn); ##postbuild ||= []; ##postbuild << fn; end
end
end
class MyBuilder < AbstractBuilder
before_build :preprocess
after_build :postprocess
def build; puts "BUILDING"; sleep(3); end
def preprocess; puts "Preparing to build..."; end
def postprocess; puts "Done building. Thank you for waiting."; end
end
builder = MyBuilder.new
builder.construct_with_timer
# => Preparing to build...
# => BUILDING
# => Done building. Thank you for waiting.
# => !!! Build time: 3.000119
This is a textbook-definition use case for Aspect-Oriented Programming. It generally offers a cleaner separation of concerns. In this arena, Ruby offers Aquarium and AspectR. However, you may not want to add another dependency to your project. As such, you might still consider using one of the other approaches.

Resources