How do you test code that forks using rspec - ruby

I have the following code
def start_sunspot_server
unless #server
pid = fork do
STDERR.reopen("/dev/null")
STDOUT.reopen("/dev/null")
server.run
end
at_exit { Process.kill("TERM", pid) }
wait_until_solr_starts
end
end
How would I effectively go about testing it using rspec?
I thought something along
Kernel.should_receive(:fork)
STDERR.should_receive(:reopen).with("/dev/null")
STDOUT.should_receive(:reopen).with("/dev/null")
server.should_receive(:run)
etc

I'm confused by the #server instance variable and server method in your example, but here is an example that should help you get where you're trying to go:
class Runner
def run
fork do
STDERR.reopen("/dev/null")
end
end
end
describe "runner" do
it "#run reopens STDERR at /dev/null" do
runner = Runner.new
runner.should_receive(:fork) do |&block|
STDERR.should_receive(:reopen).with("/dev/null")
block.call
end
runner.run
end
end
The key is that the fork message is sent to the Runner object itself, even though its implementation is in the Kernel module.
HTH,
David

David's solution didn't work for us. Maybe it's because we're not using RSpec 2?
Here's what did work.
def run
fork do
blah
end
end
describe '#run' do
it 'should create a fork which calls #blah' do
subject.should_receive(:fork).and_yield do |block_context|
block_context.should_receive(:blah)
end
subject.run_job
end
end
I'm not sure how this would apply when calling a constant, such as STDERR, but this was the only way we were able to accomplish fork testing.

Related

Can I determine if Ruby code is executing in a Sidekiq job?

Can I do something like the following? If yes, how do I implement in_sidekiq_job?
def method_for_anyone_to_call
if in_sidekiq_job
puts "hey! I'm running in a Sidekiq job."
end
end
You can use Sidekiq.server? for that, i.e.:
def method_for_anyone_to_call
if Sidekiq.server?
puts "hey! I'm running in a Sidekiq job."
end
end

How to handle running Cucumber features within Ruby method?

I am using a AfterConfiguration hook to run some setup config before my tests start, however the issue that I am faced with is that when I run my methods one of them will run a set of feature files using backticks in a Ruby method, which in turn seems to re-initialize cucumber and repeat the process, so I am stuck in a loop
AfterConfiguration do
EnvironmentSetup::TestUsers.create_test_users
end
module EnvironmentSetup
class TestUsers
def self.create_test_users
# other logic here
`cucumber "#{path_to_feature}"` # Use backticks to run cucumber scripts in a subshell
end
end
end
So when this is executed it goes back to the beginning and runs it all my other logic again
Is there a way to only run this once, or ignore the AfterConfiguration on the second loop? declare a global variable?
I have also tried
AfterConfiguration do
if defined? $a == nil
EnvironmentSetup::RedisUsers.check_redis_users
EnvironmentSetup::TestUsers.create_test_users
end
end
module EnvironmentSetup
class TestUsers
def self.create_test_users
# other logic here
$a = true
`cucumber "#{path_to_feature}"` # Use backticks to run cucumber scripts in a subshell
end
end
end
but I'm guessing that the variable set is not being carried across when re-initializing?
Try setting Environment variable:
AfterConfiguration do
return if ENV['CUCUMBER_CONFIGURED'] == 'yes'
EnvironmentSetup::TestUsers.create_test_users
ENV['CUCUMBER_CONFIGURED'] = 'yes'
end
And run cucumber something like this:
CUCUMBER_CONFIGURED='no'; cucumber ...

log in non sidekiq class

I have a worker that delegates the work to another class like this:
class SynJob
include Sidekiq::Worker
sidekiq_options queue: :sync
def perform(user_id)
OtherClass.new(blah, blah, blah)
end
end
class OtherClass
def initialize
puts "we are in OtherClass"
end
end
My question is, how do I log to stdout from OtherClass.
My puts statements do not show up in the heroku stdout log.
The literal answer to your question is to use puts or other Ruby APIs for writing to stdout. You can call this both in your SynJob or your OtherClass code and it will execute the same, writing to the stdout of the sidekiq worker process.
However, this probably is not what you want to do. If this is a Rails app, you probably want to write to the Rails logger, which should be available both in your worker and in other code:
Rails.logger.info "I'm a debug message"
This will show up in the appropriate log both locally and when running deployed on Heroku.

Load a Ruby TestCase Without Running It

I'm trying to write a custom tool that runs ruby unit tests with my customizations.
What I need it to do is to load a certain TestCase from given file(through require or whatever), and then run it after doing some calculations and initializations.
Problem is, the moment I require "test/unit" and a test case, it runs immediately.
What can I do with this?
Thanks.
Since you're running 1.9 and test/unit in 1.9 is merely a wrapper for MiniTest, the following approach should work:
implement your own custom Runner
set MiniTest's runner to your custom runner
Something like (shameless plug from EndOfLine Custom Test Runner, adjusted to Ruby 1.9):
fastfailrunner.rb:
require 'test/unit'
class FastFailRunner19 < MiniTest::Unit
def _run args = []
puts "fast fail runner"
end
end
~
example_test.rb:
require 'test/unit'
class ExampleTest < Test::Unit::TestCase
def test_assert_equal
assert_equal 1, 1
end
def test_lies
assert false
end
def test_exceptions
raise Exception, 'Beware the Jubjub bird, and shun the frumious Bandersnatch!'
end
def test_truth
assert true
end
end
run.rb:
require_relative 'fast_fail_runner'
require_relative 'example_test'
MiniTest::Unit.runner= FastFailRunner19.new
If you run this with
ruby run.rb
the custom FastFailRunner19 will be used, which does nothing.
What about reading file content as a regular text file and doing eval on its content after you initialize/calculate things you say? It may not be sufficient for your needs and may require manual setup and execution of testing framework.
Like that (I put heredoc instead of reading file). Basically content is just a string containing your test case code.
content = <<TEST_CASE
class YourTestCase
def hello
puts 'Hello from eval'
end
end
YourTestCase.new.hello
TEST_CASE
eval content
Note: Altough I'd rather not use eval if there is another way. One should be extra careful when evaling code from string manually in any language.
You could collect the test cases you want to deferred its executions and store them in an array. Afterwards you would create a block execution code. For instance:
test_files = ['test/unit/first_test.rb'] #=> Testcases you want to run
test_block = Proc.new {spec_files.each {|f|load f} } #=> block storing the actual execution of those tests.
Once you're ready to call those testcases you just do test_block.call.
To generalize a bit, when thinking about deferring or delaying code executions, closures are a very elegant and flexible alternative.

In Ruby, is there a library to make command line scripts more user friendly, prompting me what to run?

So what I do now is my Ruby scripts have a bunch of methods, and I have to comment out which method calls I don't want to call, and un-comment the one I want to fire in my command line script.
Is there a Ruby library that makes it easy to prompt the user to which function to fire?
If you want code that will selectively execute a particular method in a source file, you'll need to write a dispatch table to take the input and call whichever routine is desired.
It's all pretty standard stuff for a programmer and easily done with OptionParser and some creative use of case/when statements.
You can use ARGV to iterate over your program's arguments. There is also Getopt::Declare, which is a library for dealing with command line arguments in a similar fashion to traditional Unix utilities.
If you need something that would really prompt user during the execution of the script, you can wrap the methods you need to prompt about:
module Confirmable
def confirm_first *methods
methods.each do |meth|
alias_method "orig_#{meth}", meth
define_method meth do |*args, &block|
print "Execute #{meth}?[Yn] "
s = gets.chomp
return if s.downcase == 'n'
send "orig_#{meth}", *args, &block
end
end
end
end
class MyClass
extend Confirmable
def foo arg
puts "starting foo"
puts arg
end
confirm_first :foo
end
c = MyClass.new
c.foo "bar"
This way, each method that you mark with confirm_first will first ask you for confirmation before actually executing.

Resources