Running RSpec from within my Ruby application

Hi all

(I tried to send this question to the list a few hours ago, but it
seems to have not got through. If it did sorry for the duplication.)

Does anyone have any tips or examples of how to run RSpec example
groups programmatically from within a Ruby application?

Essentially I would like to be able to:

  1. load example groups from a set of files I know about
  2. run them
  3. get back an object that contains information about successes and
    failures
  4. reload the example groups

I think I i can accomplish (3) with a custom formatter, (4) by
reflecting on and undefining the classes Example::Subclass_1 etc as
required.

But I’m having real trouble with the first two steps. Here’s what I have
so far:

require ‘rubygems’
require ‘spec’

$spec_runner_at_exit_hook_registered = true
require ‘some_specs’

err = StringIO.new
out = StringIO.new
options = Spec::runner::Options.new(err, out)

options.add_example_group(Spec::Example::ExampleGroup::Subclass_1)
runner = Spec::runner::ExampleGroupRunner.new(options)

runner.run
puts “results:”
puts out.string
puts “\n\n[script end]”

But the results string here claims that there were 0 examples run. By
looking at the output I know that my example is being run however.

Can someone help me put these pieces together?

thanks,
Dan

Well, after some time fiddling, I’ve come up with this:

require ‘rubygems’

make sure nothing gets added to ruby’s at_exit hook:

$spec_runner_at_exit_hook_registered = true

load RSpec

require ‘spec’
require ‘spec/runner/formatter/base_formatter’

helper to find all defined example groups

def lookup_example_groups
cs = []
Spec::Example::ExampleGroup.constants.each do |c|
if c =~ /Subclass_/
cs << Spec::Example::ExampleGroup.const_get©
end
end
cs
end

helper to undefine example groups

def clean_example_groups(groups)
groups.each do |c|
puts “cleaning #{c.to_s}”
Spec::Example::ExampleGroup.send(:remove_const,
c.to_s.split("::").last.intern)
end
end

my custom formatter, mainly just a stub for now, but will be used

to pass information about failing specs back to the gui.

class RubyFormatter < Spec::runner::Formatter::BaseFormatter
def initialize
super(rspec_options, StringIO.new)
@pass_count = 0
@fail_count = 0
end

def start(example_count)
  @example_count = example_count
end

def example_passed(*args)
  @pass_count += 1
end

def example_failed(*args)
  @fail_count += 1
end

def inspect
  "<RubyFormatter total:#{@example_count}, passed:#{@pass_count}, 

failed:#{@fail_count}>"
end
end

stub out options to use my own formatter

def rspec_options.formatters
@o ||= RubyFormatter.new
[@o]
end

load the actual specs

load ‘some_specs.rb’

run the example groups

lookup_example_groups.each do |eg|
eg.run
end

examine the results

p rspec_options.formatters.first

undefine example groups

clean_example_groups(lookup_example_groups)

clear up formatter

rspec_options.instance_variable_set(:@o, nil)

system is now clean (?), and ready to reload specs etc

As you can probably tell, I’m coding without really understanding. This
works well enough for my
needs right now, but please let me know how these things can be done
better.

thanks,
Dan

On Sep 27, 2008, at 8:02 AM, Daniel L. wrote:

  1. run them
  2. get back an object that contains information about successes and
    failures
  3. reload the example groups

I’m missing the why. What is the higher-level objective that you’re
trying to accomplish? Seems like you want something like autotest + a
custom formatter?

Scott

— Scott T. [email protected] wrote:

I’m missing the why. What is the higher-level objective that you’re
trying to accomplish? Seems like you want something like autotest + a
custom formatter?

The app (www.redcaride.com) has a plugin system with reloading, which
lets you work on your plugin
without restarting the application. Now I can reload and rerun the specs
too, so you really do
never have to leave the app.

I know that this is a slightly dangerous way of running specs - but it
is convenient. And I plan
to make it almost as quick to load another app instance and run your
specs in a clean fashion as
well.

best,
Dan