| 12
 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
 
 | require 'support/doubled_classes'
module RSpec
  module Mocks
    RSpec.describe "Method visibility for verified doubles" do
      include_context "with isolated configuration"
      before do
        RSpec::Mocks.configuration.verify_doubled_constant_names = true
      end
      context "for an instance double (when the class is loaded)" do
        shared_examples "preserves method visibility" do |visibility|
          method_name = :"defined_#{visibility}_method"
          it "can allow a #{visibility} instance method" do
            o = instance_double('LoadedClass')
            allow(o).to receive(method_name).and_return(3)
            expect(o.send method_name).to eq(3)
          end
          it "can expect a #{visibility} instance method" do
            o = instance_double('LoadedClass')
            expect(o).to receive(method_name)
            o.send method_name
          end
          it "preserves #{visibility} visibility when allowing a #{visibility} method" do
            preserves_visibility(method_name, visibility) do
              instance_double('LoadedClass').tap do |o|
                allow(o).to receive(method_name)
              end
            end
          end
          it "preserves #{visibility} visibility when expecting a #{visibility} method" do
            preserves_visibility(method_name, visibility) do
              instance_double('LoadedClass').tap do |o|
                expect(o).to receive(method_name).at_least(:once)
                o.send(method_name) # to satisfy the expectation
              end
            end
          end
          it "preserves #{visibility} visibility on a null object" do
            preserves_visibility(method_name, visibility) do
              instance_double('LoadedClass').as_null_object
            end
          end
        end
        include_examples "preserves method visibility", :private
        include_examples "preserves method visibility", :protected
      end
      context "for a class double (when the class is loaded)" do
        shared_examples "preserves method visibility" do |visibility|
          method_name = :"defined_#{visibility}_class_method"
          it "can allow a #{visibility} instance method" do
            o = class_double('LoadedClass')
            allow(o).to receive(method_name).and_return(3)
            expect(o.send method_name).to eq(3)
          end
          it "can expect a #{visibility} instance method" do
            o = class_double('LoadedClass')
            expect(o).to receive(method_name)
            o.send method_name
          end
          it "preserves #{visibility} visibility when allowing a #{visibility} method" do
            preserves_visibility(method_name, visibility) do
              class_double('LoadedClass').tap do |o|
                allow(o).to receive(method_name)
              end
            end
          end
          it "preserves #{visibility} visibility when expecting a #{visibility} method" do
            preserves_visibility(method_name, visibility) do
              class_double('LoadedClass').tap do |o|
                expect(o).to receive(method_name).at_least(:once)
                o.send(method_name) # to satisfy the expectation
              end
            end
          end
          it "preserves #{visibility} visibility on a null object" do
            preserves_visibility(method_name, visibility) do
              class_double('LoadedClass').as_null_object
            end
          end
        end
        include_examples "preserves method visibility", :private
        include_examples "preserves method visibility", :protected
      end
      def preserves_visibility(method_name, visibility)
        double = yield
        expect {
          # send bypasses visbility, so we use eval instead.
          eval("double.#{method_name}")
        }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility))
        expect { double.send(method_name) }.not_to raise_error
        expect { double.__send__(method_name) }.not_to raise_error
        unless double.null_object?
          # Null object doubles use `method_missing` and so the singleton class
          # doesn't know what methods are defined.
          singleton_class = class << double; self; end
          expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true
        end
      end
      RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility|
        match do |msg|
          # This should NOT Be just `msg.match(visibility)` because the method being called
          # has the visibility name in it. We want to ensure it's a message that ruby is
          # stating is of the given visibility.
          msg.match("#{visibility} ") && msg.match(method_name.to_s)
        end
      end
    end
  end
end
 |