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
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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/rspec/rails/mocks.rb', line 89 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) double("#{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, :update].each do |key| if stubs[key] == false RSpec::Mocks.(m.errors, :empty?).and_return(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.has_attribute?(attr_name) __model_class_has_column?(attr_name) end unless #{stubs.has_key?(:has_attribute?)} 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.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/rspec/rails/mocks.rb', line 236 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 |, return_value| if m.respond_to?("#{}=") m.__send__("#{}=", return_value) else RSpec::Mocks.(m, ).and_return(return_value) end end yield m if block_given? end end |