dchelimsky / rspec-rails

RSpec extension library for Ruby on Rails

This URL has Read+Write access

zdennis (author)
Thu Aug 07 08:03:55 -0700 2008
David Chelimsky (committer)
Mon Aug 11 04:37:59 -0700 2008
rspec-rails / lib / spec / rails / mocks.rb
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 1 module Spec
2 module Rails
3
4 class IllegalDataAccessException < StandardError; end
5
6 module Mocks
7
8 # Creates a mock object instance for a +model_class+ with common
9 # methods stubbed out. Additional methods may be easily stubbed (via
10 # add_stubs) if +stubs+ is passed.
11 def mock_model(model_class, options_and_stubs = {})
9a1e10e7 » adzap 2008-06-24 fix to_param in mock_model ... 12 id = options_and_stubs[:id] || next_id
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 13 options_and_stubs.reverse_merge!({
14 :id => id,
15 :to_param => id.to_s,
16 :new_record? => false,
17 :errors => stub("errors", :count => 0)
18 })
9a1e10e7 » adzap 2008-06-24 fix to_param in mock_model ... 19 m = mock("#{model_class.name}_#{id}", options_and_stubs)
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 20 m.send(:__mock_proxy).instance_eval <<-CODE
3c59e0c7 » zdennis 2008-08-07 Added mock_model#as_new_rec... 21 def @target.as_new_record
22 self.stub!(:id).and_return nil
23 self.stub!(:to_param).and_return nil
24 self.stub!(:new_record?).and_return true
25 self
26 end
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 27 def @target.is_a?(other)
28 #{model_class}.ancestors.include?(other)
29 end
30 def @target.kind_of?(other)
31 #{model_class}.ancestors.include?(other)
32 end
33 def @target.instance_of?(other)
34 other == #{model_class}
35 end
36 def @target.class
37 #{model_class}
38 end
39 CODE
40 yield m if block_given?
41 m
42 end
38542031 » dchelimsky 2008-06-09 extend stub_model object in... 43
44 module ModelStubber
45 def connection
46 raise Spec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")
47 end
48 def new_record?
49 id.nil?
50 end
51 def as_new_record
52 self.id = nil
53 self
54 end
55 end
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 56
57 # :call-seq:
58 # stub_model(Model)
59 # stub_model(Model).as_new_record
60 # stub_model(Model, hash_of_stubs)
344fc0cc » dchelimsky 2008-06-29 words 61 # stub_model(Model, instance_variable_name, hash_of_stubs)
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 62 #
63 # Creates an instance of +Model+ that is prohibited from accessing the
344fc0cc » dchelimsky 2008-06-29 words 64 # database*. For each key in +hash_of_stubs+, if the model has a
65 # matching attribute (determined by asking it) are simply assigned the
66 # submitted values. If the model does not have a matching attribute, the
67 # key/value pair is assigned as a stub return value using RSpec's
68 # mocking/stubbing framework.
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 69 #
344fc0cc » dchelimsky 2008-06-29 words 70 # <tt>new_record?</tt> is overridden to return the result of id.nil?
71 # This means that by default new_record? will return false. If you want
72 # the object to behave as a new record, sending it +as_new_record+ will
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 73 # set the id to nil. You can also explicitly set :id => nil, in which
344fc0cc » dchelimsky 2008-06-29 words 74 # case new_record? will return true, but using +as_new_record+ makes the
75 # example a bit more descriptive.
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 76 #
344fc0cc » dchelimsky 2008-06-29 words 77 # While you can use stub_model in any example (model, view, controller,
78 # helper), it is especially useful in view examples, which are
79 # inherently more state-based than interaction-based.
80 #
81 # == Database Independence
82 #
83 # +stub_model+ does not make your examples entirely
84 # database-independent. It does not stop the model class itself from
85 # loading up its columns from the database. It just prevents data access
86 # from the object itself. To completely decouple from the database, take
87 # a look at libraries like unit_record or NullDB.
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 88 #
89 # == Examples
90 #
91 # stub_model(Person)
92 # stub_model(Person).as_new_record
93 # stub_model(Person, :id => 37)
94 # stub_model(Person) do |person|
344fc0cc » dchelimsky 2008-06-29 words 95 # person.first_name = "David"
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 96 # end
344fc0cc » dchelimsky 2008-06-29 words 97 def stub_model(model_class, stubs={})
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 98 stubs = {:id => next_id}.merge(stubs)
99 returning model_class.new do |model|
100 model.id = stubs.delete(:id)
38542031 » dchelimsky 2008-06-09 extend stub_model object in... 101 model.extend ModelStubber
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 102 stubs.each do |k,v|
103 if model.has_attribute?(k)
104 model[k] = stubs.delete(k)
105 end
106 end
107 add_stubs(model, stubs)
108 yield model if block_given?
109 end
110 end
111
112 #--
113 # TODO - Shouldn't this just be an extension of stub! ??
114 # - object.stub!(:method => return_value, :method2 => return_value2, :etc => etc)
115 #++
116 # Stubs methods on +object+ (if +object+ is a symbol or string a new mock
344fc0cc » dchelimsky 2008-06-29 words 117 # with that name will be created). +stubs+ is a Hash of +method=>value+
ce49b6c7 » dchelimsky 2008-04-28 moved mock_model and stub_m... 118 def add_stubs(object, stubs = {}) #:nodoc:
119 m = [String, Symbol].index(object.class) ? mock(object.to_s) : object
120 stubs.each {|k,v| m.stub!(k).and_return(v)}
121 m
122 end
123
124 private
125 @@model_id = 1000
126 def next_id
127 @@model_id += 1
128 end
129
130 end
131 end
9a1e10e7 » adzap 2008-06-24 fix to_param in mock_model ... 132 end