rspec-rails-mocha 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|