File: expected_arg_verification_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 (160 lines) | stat: -rw-r--r-- 6,778 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
require 'support/doubled_classes'

module RSpec
  module Mocks
    RSpec.describe 'Expected argument verification (when `#with` is called)' do
      # Note: these specs here aren't meant to be exhaustive. The specs in
      # rspec-support for the method signature verifier are. Here we are just
      # covering the code paths within the `with` implementation, including
      # the special handling for `any_args` and `no_args`.
      context "when doubling an unloaded class" do
        it 'allows any arguments' do
          expect(defined?(UnloadedClass)).to be_falsey
          inst_dbl = instance_double("UnloadedClass")

          expect {
            expect(inst_dbl).to receive(:message).with(:foo, :bar, :bazz)
          }.not_to raise_error

          reset inst_dbl
        end
      end

      context "when doubling a loaded class" do
        let(:dbl) { instance_double(LoadedClass) }
        after { reset dbl }

        context "when `any_args` is used" do
          context "as the only argument" do
            it "is allowed regardless of how many args the method requires" do
              expect {
                expect(dbl).to receive(:instance_method_with_two_args).with(any_args)
              }.not_to raise_error
            end
          end

          context "as the first argument, with too many additional args" do
            it "is disallowed" do
              expect {
                expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2, 3)
              }.to fail_with("Wrong number of arguments. Expected 2, got 3.")
            end
          end

          context "as the first argument, with an allowed number of additional args" do
            it "is allowed" do
              expect {
                expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2)
              }.not_to raise_error

              expect {
                expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1)
              }.not_to raise_error
            end
          end
        end

        context "when `no_args` is used" do
          it "allows a method expectation on a method that accepts no arguments" do
            expect(LoadedClass.instance_method(:defined_instance_method).arity).to eq(0)

            expect {
              expect(dbl).to receive(:defined_instance_method).with(no_args)
            }.not_to raise_error
          end

          it "allows a method expectation on a method that has defaults for all arguments" do
            expect {
              expect(dbl).to receive(:instance_method_with_only_defaults).with(no_args)
            }.not_to raise_error
          end

          it "does not allow a method expectation on a method that has required arguments" do
            expect {
              expect(dbl).to receive(:instance_method_with_two_args).with(no_args)
            }.to fail_with("Wrong number of arguments. Expected 2, got 0.")
          end

          if RSpec::Support::RubyFeatures.required_kw_args_supported?
            context "for a method with required keyword args" do
              it 'covers the required args when `any_args` is last' do
                expect {
                  expect(dbl).to receive(:kw_args_method).with(1, any_args)
                }.not_to raise_error
              end

              it 'does not cover the required args when there are args after `any_args`' do
                expect {
                  # Use eval to avoid syntax error on 1.8 and 1.9
                  eval("expect(dbl).to receive(:kw_args_method).with(any_args, optional_arg: 3)")
                }.to fail_with("Missing required keyword arguments: required_arg")
              end
            end
          end
        end

        if RSpec::Support::RubyFeatures.required_kw_args_supported?
          it 'does not cover required args when `any_args` is not used' do
            expect {
              expect(dbl).to receive(:kw_args_method).with(anything)
            }.to fail_with("Missing required keyword arguments: required_arg")
          end
        end

        context "when a list of args is provided" do
          it "allows a method expectation when the arity matches" do
            expect {
              expect(dbl).to receive(:instance_method_with_two_args).with(1, 2)
            }.not_to raise_error
          end

          it "does not allow a method expectation with an arity mismatch" do
            expect {
              expect(dbl).to receive(:instance_method_with_two_args).with(1, 2, 3)
            }.to fail_with("Wrong number of arguments. Expected 2, got 3.")
          end
        end

        context "when `with` is called with no args" do
          it "fails with an error suggesting the user use `no_args` instead" do
            expect {
              expect(dbl).to receive(:instance_method_with_two_args).with
            }.to raise_error(ArgumentError, /no_args/)
          end
        end

        if RSpec::Support::RubyFeatures.required_kw_args_supported?
          context "for a method with keyword args" do
            it "matches against a hash submitted as keyword arguments and received as positional argument (in both Ruby 2 and Ruby 3)" do
              expect(dbl).to receive(:kw_args_method).with(1, {:required_arg => 2, :optional_arg => 3})
              dbl.kw_args_method(1, :required_arg => 2, :optional_arg => 3)
            end

            if RUBY_VERSION >= "3"
              it "fails to match against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do
                expect(dbl).to receive(:kw_args_method).with(1, :required_arg => 2, :optional_arg => 3)
                expect do
                  dbl.kw_args_method(1, {:required_arg => 2, :optional_arg => 3})
                end.to fail_with(a_string_including("expected: (1, {:optional_arg=>3, :required_arg=>2}) (keyword arguments)", "got: (1, {:optional_arg=>3, :required_arg=>2}) (options hash)"))
              end
            else
              it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do
                expect(dbl).to receive(:kw_args_method).with(1, :required_arg => 2, :optional_arg => 3)
                dbl.kw_args_method(1, {:required_arg => 2, :optional_arg => 3})
              end
            end

            context "when using `send`" do
              let(:dbl) { instance_double(Class.new { eval "def m(k:); end" }) }

              it "matches against keyword arguments" do
                expect(dbl).to receive(:m).with(:k => 1)
                dbl.send(:m, :k => 1)
              end
            end
          end
        end
      end
    end
  end
end