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
|