Module: RSpec::Rails::Mocks
- Defined in:
- lib/rspec/rails/mocks.rb
Defined Under Namespace
Modules: ActiveModelInstanceMethods, ActiveModelStubExtensions, ActiveRecordInstanceMethods, ActiveRecordStubExtensions
Instance Method Summary collapse
-
#mock_model(string_or_model_class, stubs = {}) ⇒ Object
Creates a test double representing
string_or_model_class
with common ActiveModel methods stubbed out. -
#stub_model(model_class, stubs = {}) ⇒ Object
Creates an instance of
Model
withto_param
stubbed using a generated value that is unique to each object.
Instance Method Details
#mock_model(string_or_model_class, stubs = {}) ⇒ Object
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.
NOTE that only ActiveModel's 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
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 146 147 148 |
# File 'lib/rspec/rails/mocks.rb', line 67 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 = stubs.reverse_merge(:id => next_id) stubs = stubs.reverse_merge(:persisted? => !!stubs[:id], :destroyed? => false, :marked_for_destruction? => false, :valid? => true, :blank? => false) mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m| m.singleton_class.class_eval do include ActiveModelInstanceMethods include ActiveRecordInstanceMethods if defined?(ActiveRecord) include ActiveModel::Conversion include ActiveModel::Validations end if defined?(ActiveRecord) [:save, :update_attributes].each do |key| if stubs[key] == false m.errors.stub(:empty? => false) end end end m.__send__(:__mock_proxy).instance_eval(<<-CODE, __FILE__, __LINE__) def @object.is_a?(other) #{model_class}.ancestors.include?(other) end unless #{stubs.has_key?(:is_a?)} def @object.kind_of?(other) #{model_class}.ancestors.include?(other) end unless #{stubs.has_key?(:kind_of?)} def @object.instance_of?(other) other == #{model_class} end unless #{stubs.has_key?(:instance_of?)} def @object.__model_class_has_column?(method_name) #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) end def @object.respond_to?(method_name, include_private=false) __model_class_has_column?(method_name) ? true : super end unless #{stubs.has_key?(:respond_to?)} def @object.method_missing(m, *a, &b) respond_to?(m) ? null_object? ? self : nil : super end def @object.class #{model_class} end unless #{stubs.has_key?(:class)} def @object.to_s "#{model_class.name}_#{to_param}" end unless #{stubs.has_key?(:to_s)} CODE yield m if block_given? end end |
#stub_model(model_class, stubs = {}) ⇒ Object
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.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/rspec/rails/mocks.rb', line 210 def stub_model(model_class, stubs={}) model_class.new.tap do |m| m.extend ActiveModelStubExtensions if defined?(ActiveRecord) && model_class < ActiveRecord::Base m.extend ActiveRecordStubExtensions primary_key = model_class.primary_key.to_sym stubs = stubs.reverse_merge(primary_key => next_id) stubs = stubs.reverse_merge(:persisted? => !!stubs[primary_key]) else stubs = stubs.reverse_merge(:id => next_id) stubs = stubs.reverse_merge(:persisted? => !!stubs[:id]) end stubs = stubs.reverse_merge(:blank? => false) stubs.each do |k,v| m.__send__("#{k}=", stubs.delete(k)) if m.respond_to?("#{k}=") end m.stub(stubs) yield m if block_given? end end |