rspec-rails-mocha 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- require 'spec'
1
+ require 'rspec/core'
2
2
  require 'rspec/rails/mocha'
3
3
 
4
- Spec::Runner.configure do |config|
4
+ RSpec.configure do |config|
5
5
  config.mock_with :mocha
6
- config.include Spec::Rails::Mocha
6
+ config.include RSpec::Rails::Mocha
7
7
  end
@@ -1,139 +1,207 @@
1
- # copied from rspec-rails v1.3 and tweaked to work as expected with Mocha
2
- # TODO: update to reflect the API of rspec-rails 2.0
3
- module Spec
1
+ require 'active_model'
2
+
3
+ module RSpec
4
4
  module Rails
5
- unless defined?(IllegalDataAccessException)
6
- class IllegalDataAccessException < StandardError; end
7
- end
8
-
9
5
  module Mocha
10
- # Creates a mock object instance for a +model_class+ with common
11
- # methods stubbed out. Additional methods may be easily stubbed (via
12
- # add_stubs) if +stubs+ is passed.
13
- def mock_model(model_class, options_and_stubs = {})
14
- id = options_and_stubs[:id] || next_id
15
- options_and_stubs = options_and_stubs.reverse_merge({
16
- :id => id,
17
- :to_param => id.to_s,
18
- :new_record? => false,
19
- :errors => stub("errors", :count => 0)
20
- })
21
- m = stub("#{model_class.name}_#{id}", options_and_stubs)
22
- m.instance_eval <<-CODE
23
- def as_new_record
24
- self.stubs(:id).returns(nil)
25
- self.stubs(:to_param).returns(nil)
26
- self.stubs(:new_record?).returns(true)
27
- self
28
- end
29
- def is_a?(other)
30
- #{model_class}.ancestors.include?(other)
31
- end
32
- def kind_of?(other)
33
- #{model_class}.ancestors.include?(other)
34
- end
35
- def instance_of?(other)
36
- other == #{model_class}
37
- end
38
- def class
39
- #{model_class}
40
- end
41
- CODE
42
- yield m if block_given?
43
- m
6
+ module ActiveModelInstanceMethods
7
+ def as_new_record
8
+ self.stubs(:persisted?) { false }
9
+ self.stubs(:id) { nil }
10
+ self
11
+ end
12
+
13
+ def persisted?
14
+ true
15
+ end
16
+
17
+ def respond_to?(message, include_private=false)
18
+ message.to_s =~ /_before_type_cast$/ ? false : super
19
+ end
44
20
  end
45
-
46
- module ModelStubber
47
- def connection
48
- raise IllegalDataAccessException.new("stubbed models are not allowed to access the database")
21
+
22
+ module ActiveRecordInstanceMethods
23
+ def destroy
24
+ self.stubs(:persisted?) { false }
25
+ self.stubs(:id) { nil }
49
26
  end
27
+
50
28
  def new_record?
51
- id.nil?
29
+ !persisted?
30
+ end
31
+ end
32
+
33
+ # Creates a test double representing +string_or_model_class+ with common
34
+ # ActiveModel methods stubbed out. Additional methods may be easily
35
+ # stubbed (via add_stubs) if +stubs+ is passed. This is most useful for
36
+ # impersonating models that don't exist yet.
37
+ #
38
+ # NOTE that only ActiveModel's methods, plus <tt>new_record?</tt>, are
39
+ # stubbed out implicitly. <tt>new_record?</tt> returns the inverse of
40
+ # <tt>persisted?</tt>, and is present only for compatibility with
41
+ # extension frameworks that have yet to update themselves to the
42
+ # ActiveModel API (which declares <tt>persisted?</tt>, not
43
+ # <tt>new_record?</tt>).
44
+ #
45
+ # +string_or_model_class+ can be any of:
46
+ #
47
+ # * A String representing a Class that does not exist
48
+ # * A String representing a Class that extends ActiveModel::Naming
49
+ # * A Class that extends ActiveModel::Naming
50
+ def mock_model(string_or_model_class, stubs = {})
51
+ if String === string_or_model_class
52
+ if Object.const_defined?(string_or_model_class)
53
+ model_class = Object.const_get(string_or_model_class)
54
+ else
55
+ model_class = Object.const_set(string_or_model_class, Class.new do
56
+ extend ActiveModel::Naming
57
+ end)
58
+ end
59
+ else
60
+ model_class = string_or_model_class
61
+ end
62
+
63
+ unless model_class.kind_of? ActiveModel::Naming
64
+ raise ArgumentError.new <<-EOM
65
+ The mock_model method can only accept as its first argument:
66
+ * A String representing a Class that does not exist
67
+ * A String representing a Class that extends ActiveModel::Naming
68
+ * A Class that extends ActiveModel::Naming
69
+
70
+ It received #{model_class.inspect}
71
+ EOM
72
+ end
73
+
74
+ stubs = stubs.reverse_merge(:id => next_id)
75
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
76
+ stubs = stubs.reverse_merge(:destroyed? => false)
77
+ stubs = stubs.reverse_merge(:marked_for_destruction? => false)
78
+ stubs = stubs.reverse_merge(:errors => stub("errors", :count => 0, :[] => [], :empty? => true))
79
+
80
+ stub("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
81
+ m.extend ActiveModelInstanceMethods
82
+ model_class.__send__ :include, ActiveModel::Conversion
83
+ model_class.__send__ :include, ActiveModel::Validations
84
+ if defined?(ActiveRecord)
85
+ m.extend ActiveRecordInstanceMethods
86
+ [:save, :update_attributes].each do |key|
87
+ if stubs[key] == false
88
+ m.errors.stubs(:empty?) { false }
89
+ end
90
+ end
91
+ end
92
+ m.instance_eval(<<-CODE, __FILE__, __LINE__)
93
+ def is_a?(other)
94
+ #{model_class}.ancestors.include?(other)
95
+ end
96
+ def kind_of?(other)
97
+ #{model_class}.ancestors.include?(other)
98
+ end
99
+ def instance_of?(other)
100
+ other == #{model_class}
101
+ end
102
+ def respond_to?(method_name, include_private=false)
103
+ #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super
104
+ end
105
+ def class
106
+ #{model_class}
107
+ end
108
+ def to_s
109
+ "#{model_class.name}_#{to_param}"
110
+ end
111
+
112
+ def to_key
113
+ [id]
114
+ end
115
+ CODE
116
+ yield m if block_given?
52
117
  end
118
+ end
119
+
120
+ module ActiveModelStubExtensions
53
121
  def as_new_record
54
- self.id = nil
122
+ self.stubs(:persisted?) { false }
123
+ self.stubs(:id) { nil }
55
124
  self
56
125
  end
57
126
  end
58
127
 
128
+ module ActiveRecordStubExtensions
129
+ def as_new_record
130
+ self.__send__("#{self.class.primary_key}=", nil)
131
+ super
132
+ end
133
+
134
+ def new_record?
135
+ !persisted?
136
+ end
137
+
138
+ def connection
139
+ raise RSpec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")
140
+ end
141
+ end
142
+
59
143
  # :call-seq:
60
144
  # stub_model(Model)
61
145
  # stub_model(Model).as_new_record
62
146
  # stub_model(Model, hash_of_stubs)
63
147
  # stub_model(Model, instance_variable_name, hash_of_stubs)
64
148
  #
65
- # Creates an instance of +Model+ that is prohibited from accessing the
66
- # database*. For each key in +hash_of_stubs+, if the model has a
67
- # matching attribute (determined by asking it) are simply assigned the
68
- # submitted values. If the model does not have a matching attribute, the
69
- # key/value pair is assigned as a stub return value using RSpec's
70
- # mocking/stubbing framework.
149
+ # Creates an instance of +Model+ with +to_param+ stubbed using a
150
+ # generated value that is unique to each object.. If +Model+ is an
151
+ # +ActiveRecord+ model, it is prohibited from accessing the database*.
152
+ #
153
+ # For each key in +hash_of_stubs+, if the model has a matching attribute
154
+ # (determined by asking it) are simply assigned the submitted values. If
155
+ # the model does not have a matching attribute, the key/value pair is
156
+ # assigned as a stub return value using RSpec's mocking/stubbing
157
+ # framework.
71
158
  #
72
- # <tt>new_record?</tt> is overridden to return the result of id.nil?
73
- # This means that by default new_record? will return false. If you want
159
+ # <tt>persisted?</tt> is overridden to return the result of !id.nil?
160
+ # This means that by default persisted? will return true. If you want
74
161
  # the object to behave as a new record, sending it +as_new_record+ will
75
162
  # set the id to nil. You can also explicitly set :id => nil, in which
76
- # case new_record? will return true, but using +as_new_record+ makes the
163
+ # case persisted? will return false, but using +as_new_record+ makes the
77
164
  # example a bit more descriptive.
78
165
  #
79
166
  # While you can use stub_model in any example (model, view, controller,
80
167
  # helper), it is especially useful in view examples, which are
81
168
  # inherently more state-based than interaction-based.
82
169
  #
83
- # == Database Independence
84
- #
85
- # +stub_model+ does not make your examples entirely
86
- # database-independent. It does not stop the model class itself from
87
- # loading up its columns from the database. It just prevents data access
88
- # from the object itself. To completely decouple from the database, take
89
- # a look at libraries like unit_record or NullDB.
90
- #
91
170
  # == Examples
92
171
  #
93
172
  # stub_model(Person)
94
173
  # stub_model(Person).as_new_record
95
- # stub_model(Person, :id => 37)
174
+ # stub_model(Person, :to_param => 37)
96
175
  # stub_model(Person) do |person|
97
176
  # person.first_name = "David"
98
177
  # end
99
178
  def stub_model(model_class, stubs={})
100
- stubs = {:id => next_id}.merge(stubs)
101
- model_class.new.tap do |model|
102
- model.id = stubs.delete(:id)
103
- model.extend ModelStubber
179
+ model_class.new.tap do |m|
180
+ m.extend ActiveModelStubExtensions
181
+ if defined?(ActiveRecord) && model_class < ActiveRecord::Base
182
+ m.extend ActiveRecordStubExtensions
183
+ primary_key = model_class.primary_key.to_sym
184
+ stubs = stubs.reverse_merge(primary_key => next_id)
185
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[primary_key])
186
+ else
187
+ stubs = stubs.reverse_merge(:id => next_id)
188
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
189
+ end
104
190
  stubs.each do |k,v|
105
- if model.has_attribute?(k)
106
- model[k] = stubs.delete(k)
107
- end
191
+ m.__send__("#{k}=", stubs.delete(k)) if m.respond_to?("#{k}=")
108
192
  end
109
- model.stubs(stubs)
110
- yield model if block_given?
193
+ m.stubs(stubs)
194
+ yield m if block_given?
111
195
  end
112
196
  end
113
-
114
- # DEPRECATED - use object.stubs(:method => value, :method2 => value)
115
- #
116
- # Stubs methods on +object+ (if +object+ is a symbol or string a new mock
117
- # with that name will be created). +stubs+ is a Hash of +method=>value+
118
- def add_stubs(object, stubs = {}) #:nodoc:
119
- Kernel.warn <<-WARNING
120
- DEPRECATION NOTICE: add_stubs is deprecated and will be removed
121
- from a future version of rspec-rails. Use this instead:
122
-
123
- object.stubs(:method => value, :method2 => value)
124
-
125
- WARNING
126
- object.stubs(stubs)
127
- end
128
197
 
129
- private
198
+ private
130
199
 
131
- @@model_id = 1000
132
-
133
- def next_id
134
- @@model_id += 1
135
- end
200
+ @@model_id = 1000
136
201
 
202
+ def next_id
203
+ @@model_id += 1
204
+ end
137
205
  end
138
206
  end
139
207
  end
@@ -1,8 +1,25 @@
1
- require 'spec/autorun'
1
+ require 'rspec'
2
2
  require 'rspec-rails-mocha'
3
- require 'active_support/core_ext'
4
3
 
5
- class Person
4
+ # Simple stub for ActiveRecord::Base
5
+ module ActiveRecord
6
+ class Base
7
+ extend ActiveModel::Naming
8
+ class << self
9
+ def primary_key; "id"; end
10
+ end
11
+ end
12
+ end
13
+
14
+ module RSpec::Rails
15
+ # usually defined in "rspec/rails/mocks"
16
+ class IllegalDataAccessException < StandardError; end
17
+ end
18
+
19
+ require 'active_support/core_ext/hash/reverse_merge'
20
+ require 'active_support/core_ext/object'
21
+
22
+ class Person < ActiveRecord::Base
6
23
  attr_accessor :id, :name
7
24
 
8
25
  def has_attribute?(attr)
@@ -42,6 +59,13 @@ describe "rspec-rails Mocha plugin" do
42
59
  person.id.should == 66
43
60
  end
44
61
 
62
+ it "should use given ID in to_key" do
63
+ personA = create(Person, :id => 66)
64
+ personA.to_key.should == [66]
65
+ personB = create(Person)
66
+ personB.to_key.first.should >= 1000
67
+ end
68
+
45
69
  it "should mock a record with properties" do
46
70
  hacker = create(Hacker, :name => "Mislav", :skillz => 1337)
47
71
  hacker.name.should == "Mislav"
@@ -66,6 +90,8 @@ describe "rspec-rails Mocha plugin" do
66
90
 
67
91
  it "should have no errors" do
68
92
  create(Person).errors.count.should == 0
93
+ create(Person).errors.empty?.should be_true
94
+ create(Person).errors[:name].should == []
69
95
  end
70
96
  end
71
97
 
@@ -79,8 +105,8 @@ describe "rspec-rails Mocha plugin" do
79
105
  it "can't connect" do
80
106
  lambda {
81
107
  create(Person).connection
82
- }.should raise_error(Spec::Rails::IllegalDataAccessException)
108
+ }.should raise_error(RSpec::Rails::IllegalDataAccessException)
83
109
  end
84
110
  end
85
111
 
86
- end
112
+ end
metadata CHANGED
@@ -5,17 +5,18 @@ version: !ruby/object:Gem::Version
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 2
10
- version: 0.2.2
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - "Mislav Marohni\xC4\x87"
14
+ - Paul Rosania
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-02-20 00:00:00 +01:00
19
+ date: 2011-02-13 00:00:00 +01:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -42,12 +43,12 @@ dependencies:
42
43
  requirements:
43
44
  - - ">="
44
45
  - !ruby/object:Gem::Version
45
- hash: 31
46
+ hash: 15
46
47
  segments:
47
- - 1
48
- - 3
49
48
  - 2
50
- version: 1.3.2
49
+ - 0
50
+ - 0
51
+ version: 2.0.0
51
52
  type: :runtime
52
53
  version_requirements: *id002
53
54
  description: Ports functionality of mock_model and stub_model from rspec-rails using Mocha.