File: matchers_spec.rb

package info (click to toggle)
ruby-rspec 3.13.0c0e0m0s1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,856 kB
  • sloc: ruby: 70,868; sh: 1,423; makefile: 99
file content (158 lines) | stat: -rw-r--r-- 5,294 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
main = self
RSpec.describe RSpec::Matchers do
  include ::RSpec::Support::InSubProcess

  describe ".configuration" do
    it 'returns a memoized configuration instance' do
      expect(RSpec::Matchers.configuration).to be_a(RSpec::Expectations::Configuration)
      expect(RSpec::Matchers.configuration).to be(RSpec::Matchers.configuration)
    end
  end

  it 'can be mixed into `main`' do
    in_sub_process do
      allow_warning if RSpec::Support::Ruby.mri? && RUBY_VERSION[0, 3] == '1.9'

      main.instance_eval do
        include RSpec::Matchers
        include RSpec::Matchers::FailMatchers

        expect(3).to eq(3)
        expect(3).to be_odd

        expect {
          expect(4).to be_zero
        }.to fail_with("expected `4.zero?` to return true, got false")
      end
    end
  end

  context "when included into a superclass after a subclass has already included it" do
    if RSpec::Support::Ruby.mri? && RUBY_VERSION[0, 3] == '1.9'
      desc_start = "print"
      matcher_method = :output
    else
      desc_start = "does not print"
      matcher_method = :avoid_outputting
    end

    it "#{desc_start} a warning so the user is made aware of the MRI 1.9 bug that can cause infinite recursion" do
      superclass = stub_const("Superclass", Class.new)
      stub_const("Subclass", Class.new(superclass) { include RSpec::Matchers })

      expect {
        superclass.send(:include, RSpec::Matchers)
      }.to send(matcher_method, a_string_including(
        "Superclass", "Subclass", "has been included"
      )).to_stderr
    end

    it "does not warn when this is a re-inclusion" do
      superclass = stub_const("Superclass", Class.new { include RSpec::Matchers })
      stub_const("Subclass", Class.new(superclass) { include RSpec::Matchers })

      expect {
        superclass.send(:include, RSpec::Matchers)
      }.to avoid_outputting.to_stderr
    end
  end

  describe "#respond_to?" do
    it "handles dynamic matcher methods" do
      expect(self).to respond_to(:be_happy, :have_eyes_closed)
    end

    it "supports the optional `include_private` arg" do
      expect(respond_to?(:puts, true)).to eq true
      expect(respond_to?(:puts, false)).to eq false
      expect(respond_to?(:puts)).to eq false
    end

    it "allows `method` to get dynamic matcher methods", :if => RUBY_VERSION.to_f >= 1.9 do
      expect(method(:be_happy).call).to be_a(be_happy.class)
    end
  end
end

module RSpec
  module Matchers
    RSpec.describe ".is_a_matcher?" do
      it 'does not match BasicObject', :if => RUBY_VERSION.to_f > 1.8 do
        expect(RSpec::Matchers.is_a_matcher?(BasicObject.new)).to eq(false)
      end

      it 'is registered with RSpec::Support' do
        expect(RSpec::Support.is_a_matcher?(be_even)).to eq(true)
      end

      it 'does not match a multi-element array' do
        # our original implementation regsitered the matcher definition as
        # `&RSpec::Matchers.method(:is_a_matcher?)`, which has a bug
        # on 1.8.7:
        #
        # irb(main):001:0> def foo(x); end
        # => nil
        # irb(main):002:0> method(:foo).call([1, 2, 3])
        # => nil
        # irb(main):003:0> method(:foo).to_proc.call([1, 2, 3])
        # ArgumentError: wrong number of arguments (3 for 1)
        #   from (irb):1:in `foo'
        #   from (irb):1:in `to_proc'
        #   from (irb):3:in `call'
        #
        # This spec guards against a regression for that case.
        expect(RSpec::Support.is_a_matcher?([1, 2, 3])).to eq(false)
      end
    end

    RSpec.describe "built in matchers" do
      let(:matchers) do
        BuiltIn.constants.map { |n| BuiltIn.const_get(n) }.select do |m|
          m.method_defined?(:matches?) && m.method_defined?(:failure_message)
        end
      end

      specify "they all have defined #=== so they can be composable" do
        missing_threequals = matchers.select do |m|
          m.instance_method(:===).owner == ::Kernel
        end

        # This spec is merely to make sure we don't forget to make
        # a built-in matcher implement `===`. It doesn't check the
        # semantics of that. Use the "an RSpec value matcher" and
        # "an RSpec block-only matcher" shared example groups to
        # actually check the semantics.
        expect(missing_threequals).to eq([])
      end

      specify "they all have defined #and and #or so they support compound expectations" do
        noncompound_matchers = matchers.reject do |m|
          m.method_defined?(:and) || m.method_defined?(:or)
        end

        expect(noncompound_matchers).to eq([])
      end

      shared_examples "a well-behaved method_missing hook" do
        include MinitestIntegration

        it "raises a NoMethodError (and not SystemStackError) for an undefined method" do
          with_minitest_loaded do
            expect { subject.some_undefined_method }.to raise_error(NoMethodError)
          end
        end
      end

      describe "RSpec::Matchers method_missing hook", :slow do
        subject { self }

        it_behaves_like "a well-behaved method_missing hook"

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