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.
- data/lib/rspec-rails-mocha.rb +3 -3
- data/lib/rspec/rails/mocha.rb +163 -95
- data/spec/mocha_spec.rb +31 -5
- metadata +9 -8
data/lib/rspec-rails-mocha.rb
CHANGED
data/lib/rspec/rails/mocha.rb
CHANGED
@@ -1,139 +1,207 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
47
|
-
def
|
48
|
-
|
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
|
-
|
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.
|
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+
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# key
|
70
|
-
#
|
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>
|
73
|
-
# This means that by default
|
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
|
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, :
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
106
|
-
model[k] = stubs.delete(k)
|
107
|
-
end
|
191
|
+
m.__send__("#{k}=", stubs.delete(k)) if m.respond_to?("#{k}=")
|
108
192
|
end
|
109
|
-
|
110
|
-
yield
|
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
|
-
|
198
|
+
private
|
130
199
|
|
131
|
-
|
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
|
data/spec/mocha_spec.rb
CHANGED
@@ -1,8 +1,25 @@
|
|
1
|
-
require '
|
1
|
+
require 'rspec'
|
2
2
|
require 'rspec-rails-mocha'
|
3
|
-
require 'active_support/core_ext'
|
4
3
|
|
5
|
-
|
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(
|
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
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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-
|
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:
|
46
|
+
hash: 15
|
46
47
|
segments:
|
47
|
-
- 1
|
48
|
-
- 3
|
49
48
|
- 2
|
50
|
-
|
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.
|