# File lib/rspec/active_model/mocks/mocks.rb, line 101 def self.primary_key; :id; end
module RSpec::ActiveModel::Mocks::Mocks
Public Class Methods
Public Instance Methods
Creates a test double representing `string_or_model_class` with common ActiveModel
methods stubbed out. Additional methods may be easily stubbed (via add_stubs) if `stubs` is passed. This is most useful for impersonating models that don't exist yet.
ActiveModel
methods, plus new_record?
, are stubbed out implicitly. new_record?
returns the inverse of persisted?
, and is present only for compatibility with extension frameworks that have yet to update themselves to the ActiveModel
API (which declares persisted?
, not new_record?
).
`string_or_model_class` can be any of:
* A String representing a Class that does not exist * A String representing a Class that extends ActiveModel::Naming * A Class that extends ActiveModel::Naming
# File lib/rspec/active_model/mocks/mocks.rb, line 94 def mock_model(string_or_model_class, stubs = {}) if String === string_or_model_class if Object.const_defined?(string_or_model_class) model_class = Object.const_get(string_or_model_class) else model_class = Object.const_set(string_or_model_class, Class.new do extend ::ActiveModel::Naming def self.primary_key; :id; end end) end else model_class = string_or_model_class end unless model_class.kind_of? ::ActiveModel::Naming raise ArgumentError.new <<-EOM The mock_model method can only accept as its first argument: * A String representing a Class that does not exist * A String representing a Class that extends ActiveModel::Naming * A Class that extends ActiveModel::Naming It received #{model_class.inspect} EOM end stubs = {:id => next_id}.merge(stubs) stubs = {:persisted? => !!stubs[:id], :destroyed? => false, :marked_for_destruction? => false, :valid? => true, :blank? => false}.merge(stubs) double("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m| msingleton = class << m; self; end msingleton.class_eval do include ActiveModelInstanceMethods include ActiveRecordInstanceMethods if defined?(ActiveRecord) include ActiveModel::Conversion include ActiveModel::Validations end if defined?(ActiveRecord) [:save, :update_attributes, :update].each do |key| if stubs[key] == false RSpec::Mocks.allow_message(m.errors, :empty?).and_return(false) end end end msingleton.__send__(:define_method, :is_a?) do |other| model_class.ancestors.include?(other) end unless stubs.has_key?(:is_a?) msingleton.__send__(:define_method, :kind_of?) do |other| model_class.ancestors.include?(other) end unless stubs.has_key?(:kind_of?) msingleton.__send__(:define_method, :instance_of?) do |other| other == model_class end unless stubs.has_key?(:instance_of?) msingleton.__send__(:define_method, :__model_class_has_column?) do |method_name| model_class.respond_to?(:column_names) && model_class.column_names.include?(method_name.to_s) end msingleton.__send__(:define_method, :has_attribute?) do |attr_name| __model_class_has_column?(attr_name) end unless stubs.has_key?(:has_attribute?) msingleton.__send__(:define_method, :respond_to?) do |method_name, *args| include_private = args.first || false __model_class_has_column?(method_name) ? true : super(method_name, include_private) end unless stubs.has_key?(:respond_to?) msingleton.__send__(:define_method, :method_missing) do |m, *a, &b| respond_to?(m) ? null_object? ? self : nil : super(m, *a, &b) end msingleton.__send__(:define_method, :class) do model_class end unless stubs.has_key?(:class) mock_param = to_param msingleton.__send__(:define_method, :to_s) do "#{model_class.name}_#{mock_param}" end unless stubs.has_key?(:to_s) yield m if block_given? end end
Creates an instance of `Model` with `to_param` stubbed using a generated value that is unique to each object. If `Model` is an `ActiveRecord` model, it is prohibited from accessing the database.
For each key in `stubs`, if the model has a matching attribute (determined by `respond_to?`) it is simply assigned the submitted values. If the model does not have a matching attribute, the key/value pair is assigned as a stub return value using RSpec's mocking/stubbing framework.
persisted?
is overridden to return the result of !id.nil? This means that by default persisted? will return true. If you want the object to behave as a new record, sending it `as_new_record` will set the id to nil. You can also explicitly set :id => nil, in which case persisted? will return false, but using `as_new_record` makes the example a bit more descriptive.
While you can use stub_model
in any example (model, view, controller, helper), it is especially useful in view examples, which are inherently more state-based than interaction-based.
@example
stub_model(Person) stub_model(Person).as_new_record stub_model(Person, :to_param => 37) stub_model(Person) {|person| person.first_name = "David"}
# File lib/rspec/active_model/mocks/mocks.rb, line 243 def stub_model(model_class, stubs={}) model_class.new.tap do |m| m.extend ActiveModelStubExtensions if defined?(ActiveRecord) && model_class < ActiveRecord::Base && model_class.primary_key m.extend ActiveRecordStubExtensions primary_key = model_class.primary_key.to_sym stubs = {primary_key => next_id}.merge(stubs) stubs = {:persisted? => !!stubs[primary_key]}.merge(stubs) else stubs = {:id => next_id}.merge(stubs) stubs = {:persisted? => !!stubs[:id]}.merge(stubs) end stubs = {:blank? => false}.merge(stubs) stubs.each do |message, return_value| if m.respond_to?("#{message}=") m.__send__("#{message}=", return_value) else RSpec::Mocks.allow_message(m, message).and_return(return_value) end end yield m if block_given? end end
Private Instance Methods
# File lib/rspec/active_model/mocks/mocks.rb, line 273 def next_id @@model_id += 1 end