How can I make sure threads inside my class end after each rspec test? - ruby

I have a jruby class which contains a heartbeat that does something every certain number of seconds: (simplified code below)
class Client
def initialise
#interval = 30
#heartbeat = Thread.new do
begin
loop do
puts "heartbeat"
sleep #interval
end
rescue Exception => e
Thread.main.raise e
end
end
end
end
And I have a range of rspec tests that instantiate this class.
At the end of each test, I would expect the object to be destroyed, but the threads seem to remain.
At the moment I've fixed this with:
client.rb:
def kill
#heartbeat.kill
end
rspec:
after(:all) do
client.kill
end
Which seems to do the job - but this doesn't feel like the best way to do it.
What is the best way to approach this?
Using version jruby-9.1.10.0 & rspec 3.7.0
Edit:
As per http://ruby-doc.org/core-2.4.0/Thread.html I would expect the thread to normally terminate when the main thread does
In my tests I instantiate the client with
describe Client do
context 'bla' do
let(:client) do
described_class.new
end
it 'blas' do
end
end
end

You should replace after(:all) with after(:each).
Should be the correct syntax for what you want to do because after(:all) evaluates after all test cases have been run.

Related

Rspec with thread

I have a class that start a Thread when initialize and I can push some actions in this thread from public methods :
class Engine
def initialize
#actions = []
self.start_thread()
end
def push_action(action)
start = #actions.empty?
#actions.push(action)
if start
#thread.run
end
end
protected
def start_thread
#thread = Thread.new do
loop do
if #actions.empty?
Thread.stop
end
#actions.each do |act|
# [...]
end
#actions.clear
sleep 1
end
end
end
end
I'd like to test this class with RSpec to check what happen when I pass some actions. But I don't know how to do that.
Thanks in advance
OK I've found a solution but it's quite dirty :
describe Engine do
describe "#push_action play" do
it "should do the play action" do
# Construct the Engine with a mock thread
mock_thread = double("Useless Thread")
allow(mock_thread).to receive(:run)
expect(Thread).to receive(:new).and_return(mock_thread)
engine = Engine.new
allow(engine).to receive(:sleep)
# Expect that the actual play action will be processed
expect(engine).to receive(:play)
# push the action in the actions' list
engine.push_action(:play)
# Stop the thread loop when the stop() method is called
expect(Thread).to receive(:stop).and_raise(StandardError)
# Manually call again the start_thread() protected method with a yielded thread
# in order to process the action
expect(Thread).to receive(:new).and_yield
expect {engine.send(:start_thread)}.to raise_error(StandardError)
end
end
end
If someone has a better solution I'd be very pleased :)

Run cleanup step if any it block failed

When one of my it blocks fails, I want to run a cleanup step. When all of the it blocks succeed I don't want to run the cleanup step.
RSpec.describe 'my describe' do
it 'first it' do
logic_that_might_fail
end
it 'second it' do
logic_that_might_fail
end
after(:all) do
cleanup_logic if ONE_OF_THE_ITS_FAILED
end
end
How do I implement ONE_OF_THE_ITS_FAILED?
Not sure if RSpec provides something out of the box, but this would work:
RSpec.describe 'my describe' do
before(:all) do
#exceptions = []
end
after(:each) do |example|
#exceptions << example.exception
end
after(:all) do |a|
cleanup_logic if #exceptions.any?
end
# ...
end
I digged a little into the RSpec Code and found a way to monkey patch the RSpec Reporter class. Put this into your spec_helper.rb:
class RSpecHook
class << self
attr_accessor :hooked
end
def example_failed(example)
# Code goes here
end
end
module FailureDetection
def register_listener(listener, *notifications)
super
return if ::RSpecHook.hooked
#listeners[:example_failed] << ::RSpecHook.new
::RSpecHook.hooked = true
end
end
RSpec::Core::Reporter.prepend FailureDetection
Of course it gets a little more complex if you wish to execute different callbacks depending on the spec you're running at the moment.
Anyway, this way you do not have to mess up your testing code with exceptions or counters to detect failures.

How to write an integration test for a loop?

I am having difficulty writing integration (no stubbing) tests for the following scenario: a process (rake task) that runs in a loop, emitting some values. Below is an approximation of the use case.
The test will succeed if I control-C it, but I would like it to catch the success condition and stop.
Anyone has some good suggestions? (stubbing/mocking are not good suggestions). I guess may be there is a way to instruct RSpec to stop a process after a matcher returns success?
describe 'rake reactor' do
it 'eventually returns 0.3' do
expect { Rake::Task['reactor'].execute }.to output(/^0\.3.*/).to_stdout
end
end
class Reactor
def initialize
#stop = false
end
def call
loop do
break if stop?
sleep random_interval
yield random_interval
end
end
def stop
#stop = true
end
def stop?
#stop == true
end
def random_interval
rand(0.1..0.4)
end
end
desc 'Start reactor'
task reactor: :environment do
reactor = Reactor.new
trap(:INT) do
reactor.stop
end
reactor.call { |m| p m }
end
A naïve way to handle it is to start a new thread and send INT from there after some predefined timeout:
before do
Thread.new do
sleep 0.5
Process.kill('INT', Process.pid)
end
end

How to rspec threaded code?

Starting using rspec I have difficulties trying to test threaded code.
Here is a simplicfication of a code founded, and I made it cause i need a Queue with Timeout capabilities
require "thread"
class TimeoutQueue
def initialize
#lock = Mutex.new
#items = []
#new_item = ConditionVariable.new
end
def push(obj)
#lock.synchronize do
#items.push(obj)
#new_item.signal
end
end
def pop(timeout = :never)
timeout += Time.now unless timeout == :never
#lock.synchronize do
loop do
time_left = timeout == :never ? nil : timeout - Time.now
if #items.empty? and time_left.to_f >= 0
#new_item.wait(#lock, time_left)
end
return #items.shift unless #items.empty?
next if timeout == :never or timeout > Time.now
return nil
end
end
end
alias_method :<<, :push
end
But I can't find a way to test it using rspec. Is there any effective documentation on testing threaded code? Any gem that can helps me?
I'm a bit blocked, thanks in advance
When unit-testing we don't want any non-deterministic behavior to affect our tests, so when testing threading we should not run anything in parallel.
Instead, we should isolate our code, and simulate the cases we want to test, by stubbing #lock, #new_item, and perhaps even Time.now (to be more readable I've taken the liberty to imagine you also have attr_reader :lock, :new_item):
it 'should signal after push' do
allow(subject.lock).to receive(:synchronize).and_yield
expect(subject.new_item).to receive(:signal)
subject.push('object')
expect(subject.items).to include('object')
end
it 'should time out if taken to long to enter synchronize loop' do
#now = Time.now
allow(Time).to receive(:now).and_return(#now, #now + 10.seconds)
allow(subject.items).to receive(:empty?).and_return true
allow(subject.lock).to receive(:synchronize).and_yield
expect(subject.new_item).to_not receive(:wait)
expect(subject.pop(5.seconds)).to be_nil
end
etc...

testing threaded code in ruby

I'm writing a delayed_job clone for DataMapper. I've got what I think is working and tested code except for the thread in the worker process. I looked to delayed_job for how to test this but there are now tests for that portion of the code. Below is the code I need to test. ideas? (I'm using rspec BTW)
def start
say "*** Starting job worker #{#name}"
t = Thread.new do
loop do
delay = Update.work_off(self) #this method well tested
break if $exit
sleep delay
break if $exit
end
clear_locks
end
trap('TERM') { terminate_with t }
trap('INT') { terminate_with t }
trap('USR1') do
say "Wakeup Signal Caught"
t.run
end
see also this thread
The best approach, I believe, is to stub the Thread.new method, and make sure that any "complicated" stuff is in it's own method which can be tested individually. Thus you would have something like this:
class Foo
def start
Thread.new do
do_something
end
end
def do_something
loop do
foo.bar(bar.foo)
end
end
end
Then you would test like this:
describe Foo
it "starts thread running do_something" do
f = Foo.new
expect(Thread).to receive(:new).and_yield
expect(f).to receive(:do_something)
f.start
end
it "do_something loops with and calls foo.bar with bar.foo" do
f = Foo.new
expect(f).to receive(:loop).and_yield #for multiple yields: receive(:loop).and_yield.and_yield.and_yield...
expect(foo).to receive(:bar).with(bar.foo)
f.do_something
end
end
This way you don't have to hax around so much to get the desired result.
You could start the worker as a subprocess when testing, waiting for it to fully start, and then check the output / send signals to it.
I suspect you can pick up quite a few concrete testing ideas in this area from the Unicorn project.
Its impossible to test threads completely. Best you can do is to use mocks.
(something like)
object.should_recieve(:trap).with('TERM').and yield
object.start
How about just having the thread yield right in your test.
Thread.stub(:new).and_yield
start
# assertions...

Resources