File: mocks_spec.rb

package info (click to toggle)
ruby-rspec 3.9.0c2e2m1s3-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,612 kB
  • sloc: ruby: 67,456; sh: 1,572; makefile: 98
file content (229 lines) | stat: -rw-r--r-- 6,980 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
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
require 'rspec/support/spec/library_wide_checks'

RSpec.describe RSpec::Mocks do
  lib_preamble = [
    # We define minitest constants because rspec/mocks/minitest_integration
    # expects these constants to already be defined.
    "module Minitest; class Assertion; end; module Test; end; end",
    'require "rspec/mocks"',
    # Must be required before other files due to how our autoloads are setup.
    # (Users won't hit this problem because they won't require all the files
    # individually in whatever order the file system returns)
    'require "rspec/mocks/any_instance"'
  ]

  # On 1.9.2 we load securerandom to get around the lack of `BasicObject#__id__.
  # Loading securerandom loads many other stdlibs it depends on. Rather than
  # declaring it (and all the stdlibs it loads) as allowed, it's easier just
  # to prevent the loading of securerandom by faking out `BasicObject#__id__
  lib_preamble.unshift "class BasicObject; def __id__; end; end" if RUBY_VERSION == '1.9.2'

  it_behaves_like 'library wide checks', 'rspec-mocks',
    :preamble_for_lib => lib_preamble,
    :allowed_loaded_feature_regexps => [
      /rbconfig/ # loaded by rspec-support
    ] do

      if RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.7\.0/
        before(:example, :description => /spec files/) do
          pending "JRuby 9.1.7.0 currently generates a circular warning which" \
                  " is unrelated to our suite."
        end
      end

    if RUBY_VERSION == '1.9.2'
      before(:example, :description => /spec files/) do
        pending "Loading psych and syck on 1.9.2 (as our test suite does) triggers warnings"
      end
    end
  end

  describe ".verify" do
    it "delegates to the space" do
      foo = double
      expect(foo).to receive(:bar)
      expect do
        RSpec::Mocks.verify
      end.to fail

      RSpec::Mocks.teardown # so the mocks aren't re-verified after this example
    end
  end

  describe ".teardown" do
    it "resets method stubs" do
      string = "foo".dup
      allow(string).to receive(:bar)
      RSpec::Mocks.teardown
      expect { string.bar }.to raise_error(NoMethodError)
    end

    it "does not put rspec-mocks into an inconsistent state when called extra times" do
      RSpec::Mocks.teardown
      RSpec::Mocks.teardown
      RSpec::Mocks.teardown

      string = "foo".dup
      expect { allow(string).to receive(:bar) }.to raise_error(RSpec::Mocks::OutsideOfExampleError)

      RSpec::Mocks.setup
      allow(string).to receive(:bar).and_return(:baz)
      expect(string.bar).to eq(:baz)
    end
  end

  describe ".setup" do
    it 'starts a new space scope that is later removed by .teardown' do
      old_space = RSpec::Mocks.space
      RSpec::Mocks.setup

      new_space = RSpec::Mocks.space
      expect(new_space).not_to equal(old_space)

      RSpec::Mocks.teardown
      expect(RSpec::Mocks.space).to equal(old_space)
    end
  end

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

  describe ".with_temporary_scope" do
    context "in a before(:all) with a stubbed double" do
      before(:all) do
        RSpec::Mocks.with_temporary_scope do
          @calculator = double
          allow(@calculator).to receive(:add) { |a, b| a + b }
          @sum = @calculator.add(3, 4)
        end

        capture_error { @calculator.add(1, 10) }
      end

      it 'allows the stubbed double to be used' do
        expect(@sum).to eq(7)
      end

      it 'does not allow the double to be used in the examples' do
        expect {
          @calculator.add(1, 2)
        }.to raise_error(RSpec::Mocks::ExpiredTestDoubleError)
      end

      it 'does not allow the double to be used after the scope in before(:all)' do
        expect(@error).to be_a(RSpec::Mocks::OutsideOfExampleError)
      end
    end

    context "in a before(:all) with a stubbed const" do
      before(:all) do
        RSpec::Mocks.with_temporary_scope do
          stub_const("ValueX", 3)
          stub_const("ValueY", 4)
          @sum = ValueX + ValueY
        end

        capture_error { ValueX + ValueY }
      end

      it 'allows the stubbed constants to be used' do
        expect(@sum).to eq(7)
      end

      it 'does not allow the stubbed constants to be used in the examples' do
        expect(defined?(ValueX)).to be_falsey
        expect(defined?(ValueY)).to be_falsey
      end

      it 'does not allow the stubbed constants to be used after the scope in before(:all)' do
        expect(@error).to be_a(NameError)
        expect(@error.message).to include("ValueX")
      end
    end

    context "in a before(:all) with an unmet mock expectation" do
      before(:all) do
        capture_error do
          RSpec::Mocks.with_temporary_scope do
            calculator = double
            expect(calculator).to receive(:add)
          end
        end
      end

      it 'fails with a mock expectation error' do
        expect(@error).to be_a(RSpec::Mocks::MockExpectationError)
      end
    end

    context "in a before(:all) with an any_instance stub" do
      before(:all) do
        RSpec::Mocks.with_temporary_scope do
          allow_any_instance_of(String).to receive(:sum_with) { |val, x| val + x }
          @sum = "foo".dup.sum_with("bar")
        end

        capture_error { "you".dup.sum_with("me") }
      end

      it 'allows the stub to be used' do
        expect(@sum).to eq("foobar")
      end

      it 'does not allow the double to be used in the examples' do
        expect {
          "foo".sum_with("baz")
        }.to raise_error(NameError, /sum_with/)
      end

      it 'does not allow the double to be used after the scope in before(:all)' do
        expect(@error).to be_a(NameError)
        expect(@error.message).to include("sum_with")
      end
    end

    it 'tears down even if an error occurs' do
      foo = Object.new

      expect {
        RSpec::Mocks.with_temporary_scope do
          allow(foo).to receive(:bar)
          raise "boom"
        end
      }.to raise_error("boom")

      expect(foo).not_to respond_to(:bar)
    end

    it 'does not verify if an error occurs before the block completes' do
      expect {
        RSpec::Mocks.with_temporary_scope do
          foo   = Object.new
          expect(foo).to receive(:bar)
          raise "boom"
        end
      }.to raise_error("boom") # rather than MockExpectationError
    end

    def capture_error
      yield
    rescue Exception => @error
    end
  end

  context "when there is a `let` declaration that overrides an argument matcher" do
    let(:boolean) { :from_let }

    before do
      expect(RSpec::Mocks::ArgumentMatchers.method_defined?(:boolean)).to be true
    end

    it 'allows the `let` definition to win' do
      expect(boolean).to eq(:from_let)
    end
  end
end