File: method_visibility_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 (129 lines) | stat: -rw-r--r-- 4,895 bytes parent folder | download | duplicates (5)
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
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