Skip to content

Commit

Permalink
Fix inclusion order of RSpec::Matchers in Test::Unit and MiniTest.
Browse files Browse the repository at this point in the history
Due to a bug in ruby 1.9, RSpec::Matchers must be included in the superclass (MiniTest::Unit::TestCase) before it is included in a subclass (Test::Unit::TestCase) or we may get infinite recursion and a SystemStackError from our `super` call in our method_missing hook.  See this gist for more info about the ruby 1.9 bug:

https://gist.github.com/845896

Closes #67.
  • Loading branch information
myronmarston committed Mar 28, 2011
1 parent 0a29866 commit 790a849
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 30 deletions.
13 changes: 9 additions & 4 deletions lib/rspec/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,18 @@ module RSpec
# end
#
module Matchers
# Include Matchers for other test frameworks
if defined?(Test::Unit::TestCase)
Test::Unit::TestCase.send(:include, self)
end
# Include Matchers for other test frameworks.
# Note that MiniTest _must_ come before TU because on ruby 1.9,
# T::U::TC is a subclass of MT::U::TC and a 1.9 bug can lead
# to infinite recursion from the `super` call in our method_missing
# hook. See this gist for more info:
# https://gist.github.com/845896
if defined?(MiniTest::Unit::TestCase)
MiniTest::Unit::TestCase.send(:include, self)
end
if defined?(Test::Unit::TestCase)
Test::Unit::TestCase.send(:include, self)
end
end
end

Expand Down
4 changes: 0 additions & 4 deletions spec/rspec/matchers/be_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
require 'spec_helper'

describe "should be_predicate" do
it "allows other undefined methods to raise errors as normal" do
expect { some_undefined_method }.to raise_error(NameError)
end

it "passes when actual returns true for :predicate?" do
actual = stub("actual", :happy? => true)
actual.should be_happy
Expand Down
24 changes: 2 additions & 22 deletions spec/rspec/matchers/matchers_spec.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
require 'spec_helper'

module Test
module Unit
class TestCase
end
end
end

module MiniTest
module Unit
class TestCase
end
end
end

module RSpec
describe Matchers do

Expand All @@ -25,21 +11,15 @@ module RSpec
end

context "once required" do

before(:all) do
path = File.expand_path("../../../../#{path}", __FILE__)
load File.join(path, 'lib/rspec/matchers.rb')
end

it "includes itself in Test::Unit::TestCase" do
test_unit_case = Test::Unit::TestCase.new
sample_matchers.each do |sample_matcher|
test_unit_case.should respond_to(sample_matcher)
end
end

it "includes itself in MiniTest::Unit::TestCase" do
minitest_case = MiniTest::Unit::TestCase.new
it "includes itself in MiniTest::Unit::TestCase", :if => defined?(MiniTest) do
minitest_case = MiniTest::Unit::TestCase.new(nil)
sample_matchers.each do |sample_matcher|
minitest_case.should respond_to(sample_matcher)
end
Expand Down
23 changes: 23 additions & 0 deletions spec/rspec/matchers/method_missing_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'spec_helper'

shared_examples_for "a well-behaved method_missing hook" do
it "raises a NoMethodError (and not SystemStackError) for an undefined method" do
expect { subject.some_undefined_method }.to raise_error(NoMethodError)
end
end

describe "RSpec::Matchers method_missing hook" do
subject { self }
it_behaves_like "a well-behaved method_missing hook"

context 'when invoked in a Test::Unit::TestCase' do
subject { Test::Unit::TestCase.new }
it_behaves_like "a well-behaved method_missing hook"
end

context 'when invoked in a MiniTest::Unit::TestCase', :if => defined?(MiniTest) do
subject { MiniTest::Unit::TestCase.new(nil) }
it_behaves_like "a well-behaved method_missing hook"
end
end

8 changes: 8 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ def add_to_load_path(path, prepend=false)
end
end

require 'test/unit'

# Make it easy to instantiate test cases for our specs.
# Test::Unit::TestCase#initialize is picky about what arguments it expects.
class Test::Unit::TestCase
def initialize; end
end

add_to_load_path("rspec-expectations", :prepend)
add_to_load_path("rspec-core")
add_to_load_path("rspec-mocks")
Expand Down

0 comments on commit 790a849

Please sign in to comment.