File: define_negated_matcher_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 (199 lines) | stat: -rw-r--r-- 7,954 bytes parent folder | download
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
module RSpec
  module Matchers
    RSpec.describe 'RSpec::Matchers.define_negated_matcher' do
      RSpec::Matchers.define :my_base_non_negated_matcher do
        match { |actual| actual == foo }

        def foo
          13
        end

        def description
          "my base matcher description"
        end
      end

      shared_examples "making a copy" do |copy_method|
        context "when making a copy via `#{copy_method}`" do
          it "uses a copy of the base matcher" do
            base_matcher = include(3)
            aliased = AliasedNegatedMatcher.new(base_matcher, Proc.new {})
            copy = aliased.__send__(copy_method)

            expect(copy).not_to equal(aliased)
            expect(copy.base_matcher).not_to equal(base_matcher)
            expect(copy.base_matcher).to be_a(RSpec::Matchers::BuiltIn::Include)
            expect(copy.base_matcher.expected).to eq([3])
          end

          it "copies custom matchers properly so they can work even though they have singleton behavior" do
            base_matcher = my_base_non_negated_matcher
            aliased = AliasedNegatedMatcher.new(base_matcher, Proc.new { |a| a })
            copy = aliased.__send__(copy_method)

            expect(copy).not_to equal(aliased)
            expect(copy.base_matcher).not_to equal(base_matcher)

            expect(15).to copy

            expect { expect(13).to copy }.to fail_with(/expected 13/)
          end
        end
      end

      include_examples "making a copy", :dup
      include_examples "making a copy", :clone

      RSpec::Matchers.define_negated_matcher :an_array_excluding, :include
      it_behaves_like "an RSpec value matcher", :valid_value => [1, 3], :invalid_value => [1, 2] do
        let(:matcher) { an_array_excluding(2) }
      end

      it 'works properly when composed' do
        list = 1.upto(10).to_a
        expect { list.delete(5) }.to change { list }.to(an_array_excluding 5)
      end

      describe "the failure message" do
        context "for a matcher with default failure messages" do
          RSpec::Matchers.define(:be_awesome) { match(&:awesome?) }
          RSpec::Matchers.define_negated_matcher :be_lame, :be_awesome

          context "when failing positively" do
            it "uses the phrasing from the provided defined matcher alias" do
              expect {
                expect(double(:awesome? => true, :inspect => "<Object>")).to be_lame
              }.to fail_with("expected <Object> to be lame")
            end
          end

          context "when failing negatively" do
            it "uses the phrasing from the provided defined matcher alias" do
              expect {
                expect(double(:awesome? => false, :inspect => "<Object>")).not_to be_lame
              }.to fail_with("expected <Object> not to be lame")
            end
          end

          context "when accessed via an alias that is not included in failure messages" do
            alias_method :be_fantastic, :be_awesome
            RSpec::Matchers.define_negated_matcher :be_terrible, :be_fantastic

            context "when failing positively" do
              it "uses the wrapped matcher's `failure_message_when_negated`" do
                expect {
                  expect(double(:awesome? => true, :inspect => "<Object>")).to be_terrible
                }.to fail_with("expected <Object> not to be awesome")
              end
            end

            context "when failing negatively" do
              it "uses the wrapped matcher's `failure_message`" do
                expect {
                  expect(double(:awesome? => false, :inspect => "<Object>")).not_to be_terrible
                }.to fail_with("expected <Object> to be awesome")
              end
            end
          end
        end

        context "for a matcher with a customized `failure_message_when_negated`" do
          RSpec::Matchers.define(:be_tall) do
            match(&:tall?)
            failure_message_when_negated do |actual|
              "expected #{actual.inspect} not to be tall, but was tall"
            end
          end
          RSpec::Matchers.define_negated_matcher :be_short, :be_tall

          context "when failing positively" do
            it "uses the wrapped matcher's `failure_message_when_negated` since it may include more detail" do
              expect {
                expect(double(:tall? => true, :inspect => "<Object>")).to be_short
              }.to fail_with("expected <Object> not to be tall, but was tall")
            end
          end

          context "when failing negatively" do
            it "uses the wrapped matcher's `failure_message` since it may include more detail" do
              expect {
                expect(double(:tall? => false, :inspect => "<Object>")).not_to be_short
              }.to fail_with("expected <Object> to be tall")
            end
          end
        end
      end

      context 'when no block is passed' do
        RSpec::Matchers.define :be_an_odd_number do
          match { |actual| actual.odd? }
        end
        RSpec::Matchers.define_negated_matcher :be_an_even_number, :be_an_odd_number

        it 'uses the default negated description' do
          expect(be_an_even_number.description).to eq("be an even number")
        end

        context "when matched positively" do
          it 'matches values that fail the original matcher' do
            expect { expect(22).to be_an_odd_number }.to fail_with("expected 22 to be an odd number")
            expect(22).to be_an_even_number
          end

          it "fails matches against values that pass the original matcher" do
            expect(21).to be_an_odd_number
            expect { expect(21).to be_an_even_number }.to fail_with("expected 21 to be an even number")
          end
        end

        context "when matched negatively" do
          it 'matches values that fail the original matcher' do
            expect { expect(21).not_to be_an_odd_number }.to fail_with("expected 21 not to be an odd number")
            expect(21).not_to be_an_even_number
          end

          it "fails matches against values that pass the original matcher" do
            expect(22).not_to be_an_odd_number
            expect { expect(22).not_to be_an_even_number }.to fail_with("expected 22 not to be an even number")
          end
        end
      end

      context 'when the negated description is overridden' do
        RSpec::Matchers.define :be_bigger_than_ten do
          match { |actual| actual > 10 }
        end
        RSpec::Matchers.define_negated_matcher :be_smaller_than_ten, :be_bigger_than_ten do |desc|
          "#{desc.sub('bigger', 'smaller')} (overridden)"
        end

        it 'overrides the description with the provided block' do
          expect(be_smaller_than_ten.description).to eq("be smaller than ten (overridden)")
        end

        it 'overrides the failure message with the provided block' do
          expect { expect(12).to be_smaller_than_ten }.to fail_with("expected 12 to be smaller than ten (overridden)")
        end
      end

      context "for a matcher that has custom `match_when_negated` logic" do
        RSpec::Matchers.define :matcher_with_custom_negation do |match_value|
          match { match_value }
          match_when_negated { |actual| actual == :does_not_match_true }
        end
        RSpec::Matchers.define_negated_matcher :negated_matcher_with_custom_negation, :matcher_with_custom_negation

        it "uses the `match_when_negated` logic for matching" do
          expect(:does_not_match_true).to negated_matcher_with_custom_negation(true)
          expect {
            expect(:does_not_match_false).to negated_matcher_with_custom_negation(true)
          }.to fail
        end

        it "uses the `match` logic for `expect(..).not_to`" do
          expect(:foo).not_to negated_matcher_with_custom_negation(true)
        end
      end
    end
  end
end