################################################################# Helper methods for mock containers. MockContainer is a module that is designed to be mixed into other classes, particularly testing framework test cases. Since we don't want to pollute the method namespace of the class that mixes in MockContainer, a number of MockContainer methods were moved into ContainerHelper to to isoloate the names.
Automatically add mocks for some common methods in ActiveRecord models.
# File lib/flexmock/mock_container.rb, line 238 def add_model_methods(mock, model_class, id, location) container = mock.flexmock_container mock_errors = container.flexmock("errors") mock_errors.flexmock_define_expectation(location, :count).and_return(0).by_default mock_errors.flexmock_define_expectation(location, :full_messages).and_return([]).by_default mock.flexmock_define_expectation(location, :id).and_return(id).by_default mock.flexmock_define_expectation(location, :to_params).and_return(id.to_s).by_default mock.flexmock_define_expectation(location, :new_record?).and_return(false).by_default mock.flexmock_define_expectation(location, :class).and_return(model_class).by_default mock.flexmock_define_expectation(location, :errors).and_return(mock_errors).by_default # HACK: Ruby 1.9 needs the following lambda so that model_class # is correctly bound below. lambda { } mock.flexmock_define_expectation(location, :is_a?).with(any).and_return { |other| other == model_class }.by_default mock.flexmock_define_expectation(location, :instance_of?).with(any).and_return { |other| other == model_class }.by_default mock.flexmock_define_expectation(location, :kind_of?).with(any).and_return { |other| model_class.ancestors.include?(other) }.by_default end
Create a PartialMockProxy for the given
object. Use name
as the name of the mock object.
# File lib/flexmock/mock_container.rb, line 267 def make_partial_proxy(container, obj, name, safe_mode) name ||= "flexmock(#{obj.class.to_s})" if !obj.instance_variable_defined?("@flexmock_proxy") || obj.instance_variable_get("@flexmock_proxy").nil? mock = FlexMock.new(name, container) proxy = PartialMockProxy.new(obj, mock, safe_mode) obj.instance_variable_set("@flexmock_proxy", proxy) end obj.instance_variable_get("@flexmock_proxy") end
Return the next id for mocked models.
# File lib/flexmock/mock_container.rb, line 207 def next_id @id_counter ||= 10000 @id_counter += 1 end
Build the chain of mocks for demeter style mocking.
Warning: Nasty code ahead.
This method builds a chain of mocks to support demeter style mocking. Given a mock chain of “first.second.third.last”, we must build a chain of mock methods that return the next mock in the chain. The expectation for the last method of the chain is returned as the result of the method.
Things to consider:
(1) The expectation for the “first” method must be created by the proper mechanism, which is supplied by the block parameter “block”. In other words, first expectation is created by calling the block. (This allows us to create expectations on both pure mocks and partial mocks, with the block handling the details).
(2) Although the first mock is arbitrary, the remaining mocks in the chain will always be pure mocks created specifically for this purpose.
(3) The expectations for all methods but the last in the chain will be setup to expect no parameters and to return the next mock in the chain.
(4) It could very well be the case that several demeter chains will be defined on a single mock object, and those chains could share some of the same methods (e.g. “mock.one.two.read” and “mock.one.two.write” both share the methods “one” and “two”). It is important that the shared methods return the same mocks in both chains.
# File lib/flexmock/mock_container.rb, line 313 def build_demeter_chain(mock, arg, &block) container = mock.flexmock_container names = arg.to_s.split('.') check_method_names(names) exp = nil next_exp = lambda { |n| block.call(n) } loop do method_name = names.shift.to_sym exp = mock.flexmock_find_expectation(method_name) need_new_exp = exp.nil? || names.empty? exp = next_exp.call(method_name) if need_new_exp break if names.empty? if need_new_exp mock = container.flexmock("demeter_#{method_name}") exp.with_no_args.and_return(mock) else mock = exp._return_value([]) end check_proper_mock(mock, method_name) next_exp = lambda { |n| mock.should_receive(n) } end exp end
Check that all the names in the list are valid method names.
# File lib/flexmock/mock_container.rb, line 348 def check_method_names(names) names.each do |name| fail FlexMock::UsageError, "Ill-formed method name '#{name}'" if name !~ METHOD_NAME_RE end end
Check that the given mock is a real FlexMock mock.
# File lib/flexmock/mock_container.rb, line 338 def check_proper_mock(mock, method_name) unless mock.kind_of?(FlexMock) fail FlexMock::UsageError, "Conflicting mock declaration for '#{method_name}' in demeter style mock" end end