File: mock_builder.rb

package info (click to toggle)
ruby-flexmock 3.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 836 kB
  • sloc: ruby: 7,572; makefile: 6
file content (145 lines) | stat: -rw-r--r-- 4,049 bytes parent folder | download | duplicates (3)
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
class FlexMock

  # This class contains helper methods for mock containers. Since
  # MockContainer is a module that is designed to be mixed into other
  # classes, (particularly testing framework test cases), we don't
  # want to pollute the method namespace of the class that mixes in
  # MockContainer. So we have aggressively moved a number of
  # MockContainer methods out of that class and into
  # MockBuilder to isoloate the names.
  #
  class MockBuilder
    def initialize(container)
      @container = container
    end

    def define_a_mock(location, *args, &block)
      opts = parse_creation_args(args)
      if opts.safe_mode && ! block_given?
        raise UsageError, "a block is required in safe mode"
      end

      result = create_double(opts)
      flexmock_mock_setup(opts.mock, opts, location, &block)
      run_post_creation_hooks(opts, location)
      result
    end

    FlexOpts = Struct.new(
      :name, :defs, :safe_mode, :mock,
      :domain_obj, :base_class,
      :extended, :extended_data
      ) do
      def data
        self.extended_data ||= {}
      end
    end

    # Parse the list of flexmock() arguments and populate the opts object.
    def parse_creation_args(args)
      opts = FlexOpts.new
      while ! args.empty?
        case args.first
        when Symbol
          unless parse_create_symbol(args, opts)
            opts.name = args.shift.to_s
          end
        when String, Symbol
          opts.name = args.shift.to_s
        when Hash
          opts.defs = args.shift
        when FlexMock
          opts.mock = args.shift
        else
          opts.domain_obj = args.shift
        end
      end
      set_base_class(opts)
      opts
    end

    # Create the test double based on the args options.
    def create_double(opts)
      if opts.extended
        result = opts.extended.create(container, opts)
      elsif opts.domain_obj
        result = create_partial(opts)
      else
        result = create_mock(opts)
      end
      opts.mock ||= result
      result
    end

    # Run any post creation hooks specified by an extension.
    def run_post_creation_hooks(opts, location)
      if opts.extended
        opts.extended.post_create(opts, location)
      end
    end


    # Setup the test double with its expections and such.
    def flexmock_mock_setup(mock, opts, location)
      mock.flexmock_based_on(opts.base_class) if opts.base_class
      mock.flexmock_define_expectation(location, opts.defs)
      yield(mock) if block_given?
      container.flexmock_remember(mock)
    end

    attr_reader :container
    private :container

    private

    # Set the base class if not defined and partials are based.
    def set_base_class(opts)
      if ! opts.base_class && opts.domain_obj && FlexMock.partials_are_based
        opts.base_class = opts.domain_obj.singleton_class
      end
    end

    # Handle a symbol in the flexmock() args list.
    def parse_create_symbol(args, opts)
      case args.first
      when :base, :safe
        opts.safe_mode = (args.shift == :safe)
        opts.domain_obj = args.shift
      when :on, :strict
        if args.shift == :strict
          if !opts.domain_obj
            raise ArgumentError, "cannot use :strict outside a partial mock"
          end
          opts.base_class = opts.domain_obj.singleton_class
        else
          opts.base_class = args.shift
        end
        opts.name ||= "#{opts.base_class} Mock"
      else
        CONTAINER_HELPER.extensions.each do |ext|
          handled = ext.handle(args, opts)
          return true if handled
        end
        return false
      end
      true
    end

    # Create a mock object in the options.
    def create_mock(opts)
      opts.mock ||= FlexMock.new(opts.name || "unknown", container)
      opts.mock
    end

    # Create a partial mock object in options.
    def create_partial(opts)
      opts.mock = PartialMockProxy.make_proxy_for(
        opts.domain_obj,
        container, opts.name,
        opts.safe_mode)
      opts.domain_obj
    end

  end

end