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
:call-seq: stub_model(Model) stub_model(Model).as_new_record stub_model(Model, hash_of_stubs) stub_model(Model, instance_variable_name, hash_of_stubs).
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
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 |
# File 'lib/rspec/rails/mocks.rb', line 52 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 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]) stubs = stubs.reverse_merge(:destroyed? => false) stubs = stubs.reverse_merge(:marked_for_destruction? => false) mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m| m.extend ActiveModelInstanceMethods m.singleton_class.__send__ :include, ActiveModel::Conversion m.singleton_class.__send__ :include, ActiveModel::Validations if defined?(ActiveRecord) m.extend ActiveRecordInstanceMethods [: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 def @object.kind_of?(other) #{model_class}.ancestors.include?(other) end def @object.instance_of?(other) other == #{model_class} end def @object.respond_to?(method_name, include_private=false) #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super end def @object.class #{model_class} end def @object.to_s "#{model_class.name}_#{to_param}" end CODE yield m if block_given? end end |
#stub_model(model_class, stubs = {}) ⇒ Object
:call-seq:
stub_model(Model)
stub_model(Model).as_new_record
stub_model(Model, hash_of_stubs)
stub_model(Model, instance_variable_name, hash_of_stubs)
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 hash_of_stubs
, if the model has a matching attribute (determined by asking it) are 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.
Examples
stub_model(Person)
stub_model(Person).as_new_record
stub_model(Person, :to_param => 37)
stub_model(Person) do |person|
person.first_name = "David"
end
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/rspec/rails/mocks.rb', line 179 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.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 |