rspec-rails 2.8.0.rc1 → 2.8.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/License.txt +22 -0
- data/README.md +253 -119
- data/features/README.md +0 -2
- data/features/Upgrade.md +1 -1
- data/lib/generators/rspec/install/templates/spec/spec_helper.rb +1 -1
- data/lib/generators/rspec/integration/integration_generator.rb +1 -0
- data/lib/generators/rspec/scaffold/scaffold_generator.rb +1 -0
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +23 -16
- data/lib/rspec/rails/adapters.rb +25 -13
- data/lib/rspec/rails/example/controller_example_group.rb +65 -122
- data/lib/rspec/rails/example/helper_example_group.rb +11 -36
- data/lib/rspec/rails/example/mailer_example_group.rb +1 -1
- data/lib/rspec/rails/example/rails_example_group.rb +5 -0
- data/lib/rspec/rails/example/request_example_group.rb +4 -19
- data/lib/rspec/rails/example/routing_example_group.rb +8 -10
- data/lib/rspec/rails/example/view_example_group.rb +21 -35
- data/lib/rspec/rails/extensions/active_record/base.rb +15 -12
- data/lib/rspec/rails/fixture_support.rb +1 -1
- data/lib/rspec/rails/matchers/be_a_new.rb +63 -30
- data/lib/rspec/rails/matchers/be_new_record.rb +18 -3
- data/lib/rspec/rails/matchers/have_extension.rb +26 -14
- data/lib/rspec/rails/matchers/redirect_to.rb +26 -7
- data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
- data/lib/rspec/rails/matchers/render_template.rb +27 -8
- data/lib/rspec/rails/matchers/routing_matchers.rb +72 -24
- data/lib/rspec/rails/mocks.rb +42 -34
- data/lib/rspec/rails/module_inclusion.rb +2 -1
- data/lib/rspec/rails/version.rb +1 -1
- data/lib/rspec/rails/view_assigns.rb +31 -34
- data/lib/rspec/rails/view_rendering.rb +10 -6
- data/spec/rspec/rails/example/controller_example_group_spec.rb +2 -2
- data/spec/rspec/rails/example/helper_example_group_spec.rb +5 -5
- data/spec/rspec/rails/example/view_example_group_spec.rb +5 -5
- data/spec/rspec/rails/matchers/be_a_new_spec.rb +2 -0
- data/spec/rspec/rails/matchers/be_new_record_spec.rb +2 -0
- data/spec/rspec/rails/matchers/render_template_spec.rb +3 -5
- data/spec/rspec/rails/mocks/mock_model_spec.rb +28 -0
- metadata +22 -19
@@ -1,23 +1,35 @@
|
|
1
1
|
require 'active_support/core_ext/module/aliasing'
|
2
2
|
require 'rspec/matchers/have'
|
3
3
|
|
4
|
-
module RSpec
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
module RSpec::Rails::Matchers
|
5
|
+
module HaveExtensions
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
#
|
10
|
+
# Enhances the failure message for `should have(n)` matchers
|
11
|
+
def failure_message_for_should_with_errors_on_extensions
|
12
|
+
return "expected #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}, got #{@actual}" if @collection_name == :errors_on
|
13
|
+
return "expected #{relativities[@relativity]}#{@expected} error on :#{@args[0]}, got #{@actual}" if @collection_name == :error_on
|
14
|
+
return failure_message_for_should_without_errors_on_extensions
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# Enhances the description for `should have(n)` matchers
|
20
|
+
def description_with_errors_on_extensions
|
21
|
+
return "have #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}" if @collection_name == :errors_on
|
22
|
+
return "have #{relativities[@relativity]}#{@expected} error on :#{@args[0]}" if @collection_name == :error_on
|
23
|
+
return description_without_errors_on_extensions
|
24
|
+
end
|
25
|
+
|
26
|
+
included do
|
27
|
+
alias_method_chain :failure_message_for_should, :errors_on_extensions
|
19
28
|
alias_method_chain :description, :errors_on_extensions
|
20
29
|
end
|
21
30
|
end
|
22
31
|
end
|
23
32
|
|
33
|
+
RSpec::Matchers::Have.class_eval do
|
34
|
+
include RSpec::Rails::Matchers::HaveExtensions
|
35
|
+
end
|
@@ -1,19 +1,38 @@
|
|
1
1
|
module RSpec::Rails::Matchers
|
2
2
|
module RedirectTo
|
3
|
-
|
3
|
+
class RedirectTo
|
4
|
+
include RSpec::Matchers::BaseMatcher
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
def initialize(scope, expected)
|
7
|
+
super(expected)
|
8
|
+
@scope = scope
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
+
# @api private
|
12
|
+
def matches?(actual)
|
13
|
+
match_unless_raises ActiveSupport::TestCase::Assertion do
|
14
|
+
@scope.assert_redirected_to(expected)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def failure_message_for_should
|
11
20
|
rescued_exception.message
|
12
21
|
end
|
13
22
|
|
14
|
-
|
15
|
-
|
23
|
+
# @api private
|
24
|
+
def failure_message_for_should_not
|
25
|
+
"expected not to redirect to #{expected.inspect}, but did"
|
16
26
|
end
|
17
27
|
end
|
28
|
+
|
29
|
+
# Delegates to `assert_redirected_to`
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# response.should redirect_to(:action => "new")
|
34
|
+
def redirect_to(target)
|
35
|
+
RedirectTo.new(self, target)
|
36
|
+
end
|
18
37
|
end
|
19
38
|
end
|
@@ -1,20 +1,39 @@
|
|
1
1
|
module RSpec::Rails::Matchers
|
2
2
|
module RenderTemplate
|
3
|
-
|
3
|
+
class RenderTemplateMatcher
|
4
|
+
include RSpec::Matchers::BaseMatcher
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize(scope, expected, message=nil)
|
7
|
+
super(Symbol === expected ? expected.to_s : expected)
|
8
|
+
@message = message
|
9
|
+
@scope = scope
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
+
# @api private
|
13
|
+
def matches?(*)
|
14
|
+
match_unless_raises ActiveSupport::TestCase::Assertion do
|
15
|
+
@scope.assert_template expected, @message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def failure_message_for_should
|
12
21
|
rescued_exception.message
|
13
22
|
end
|
14
23
|
|
15
|
-
|
16
|
-
|
24
|
+
# @api private
|
25
|
+
def failure_message_for_should_not
|
26
|
+
"expected not to render #{expected.inspect}, but did"
|
17
27
|
end
|
18
28
|
end
|
29
|
+
|
30
|
+
# Delegates to `assert_template`
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
#
|
34
|
+
# response.should render_template("new")
|
35
|
+
def render_template(options, message=nil)
|
36
|
+
RenderTemplateMatcher.new(self, options, message)
|
37
|
+
end
|
19
38
|
end
|
20
39
|
end
|
@@ -2,50 +2,98 @@ module RSpec::Rails::Matchers
|
|
2
2
|
module RoutingMatchers
|
3
3
|
extend RSpec::Matchers::DSL
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
class RouteToMatcher
|
6
|
+
include RSpec::Matchers::BaseMatcher
|
7
|
+
|
8
|
+
def initialize(scope, *expected)
|
9
|
+
@scope = scope
|
10
|
+
@expected_options = expected[1] || {}
|
11
|
+
if Hash === expected[0]
|
12
|
+
@expected_options.merge!(expected[0])
|
13
|
+
else
|
14
|
+
controller, action = expected[0].split('#')
|
15
|
+
@expected_options.merge!(:controller => controller, :action => action)
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
# @api private
|
20
|
+
def matches?(verb_to_path_map)
|
21
|
+
match_unless_raises ActiveSupport::TestCase::Assertion do
|
22
|
+
path, query = *verb_to_path_map.values.first.split('?')
|
23
|
+
@scope.assert_recognizes(
|
24
|
+
@expected_options,
|
25
|
+
{:method => verb_to_path_map.keys.first, :path => path},
|
26
|
+
Rack::Utils::parse_query(query)
|
27
|
+
)
|
28
|
+
end
|
21
29
|
end
|
22
30
|
|
23
|
-
|
31
|
+
# @api private
|
32
|
+
def failure_message_for_should
|
24
33
|
rescued_exception.message
|
25
34
|
end
|
35
|
+
end
|
26
36
|
|
37
|
+
# Delegates to `assert_recognizes`. Supports short-hand controller/action
|
38
|
+
# declarations (e.g. `"controller#action"`).
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
#
|
42
|
+
# { :get => "/things/special" }.should route_to(
|
43
|
+
# :controller => "things",
|
44
|
+
# :action => "special"
|
45
|
+
# )
|
46
|
+
#
|
47
|
+
# { :get => "/things/special" }.should route_to("things#special")
|
48
|
+
#
|
49
|
+
# @see http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes
|
50
|
+
def route_to(*expected)
|
51
|
+
RouteToMatcher.new(self, *expected)
|
27
52
|
end
|
28
53
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
54
|
+
class BeRoutableMatcher
|
55
|
+
include RSpec::Matchers::BaseMatcher
|
56
|
+
|
57
|
+
def initialize(scope)
|
58
|
+
@scope = scope
|
59
|
+
end
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
def matches?(path)
|
63
|
+
super(path)
|
64
|
+
match_unless_raises ActionController::RoutingError do
|
65
|
+
@routing_options = @scope.routes.recognize_path(
|
66
|
+
path.values.first, :method => path.keys.first
|
67
|
+
)
|
68
|
+
end
|
34
69
|
end
|
35
70
|
|
36
|
-
|
37
|
-
|
71
|
+
# @api private
|
72
|
+
def failure_message_for_should_not
|
73
|
+
"expected #{actual.inspect} not to be routable, but it routes to #{@routing_options.inspect}"
|
38
74
|
end
|
39
75
|
end
|
40
76
|
|
41
|
-
|
77
|
+
# Passes if the route expression is recognized by the Rails router based on
|
78
|
+
# the declarations in `config/routes.rb`. Delegates to
|
79
|
+
# `RouteSet#recognize_path`.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
#
|
83
|
+
# You can use route helpers provided by rspec-rails.
|
84
|
+
# {:get => "/a/path"}.should be_routable
|
85
|
+
# {:post => "/another/path"}.should be_routable
|
86
|
+
# {:put => "/yet/another/path"}.should_not be_routable
|
87
|
+
def be_routable
|
88
|
+
BeRoutableMatcher.new(self)
|
89
|
+
end
|
42
90
|
|
91
|
+
module RouteHelpers
|
43
92
|
%w(get post put delete options head).each do |method|
|
44
93
|
define_method method do |path|
|
45
94
|
{ method.to_sym => path }
|
46
95
|
end
|
47
96
|
end
|
48
|
-
|
49
97
|
end
|
50
98
|
end
|
51
99
|
end
|
data/lib/rspec/rails/mocks.rb
CHANGED
@@ -9,7 +9,7 @@ module RSpec
|
|
9
9
|
module Mocks
|
10
10
|
|
11
11
|
module ActiveModelInstanceMethods
|
12
|
-
# Stubs
|
12
|
+
# Stubs `persisted?` to return false and `id` to return nil
|
13
13
|
# @return self
|
14
14
|
def as_new_record
|
15
15
|
self.stub(:persisted?) { false }
|
@@ -30,7 +30,7 @@ module RSpec
|
|
30
30
|
end
|
31
31
|
|
32
32
|
module ActiveRecordInstanceMethods
|
33
|
-
# Stubs
|
33
|
+
# Stubs `persisted?` to return `false` and `id` to return `nil`.
|
34
34
|
def destroy
|
35
35
|
self.stub(:persisted?) { false }
|
36
36
|
self.stub(:id) { nil }
|
@@ -41,15 +41,15 @@ module RSpec
|
|
41
41
|
send(key)
|
42
42
|
end
|
43
43
|
|
44
|
-
# Returns the opposite of
|
44
|
+
# Returns the opposite of `persisted?`
|
45
45
|
def new_record?
|
46
46
|
!persisted?
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
# Creates a test double representing
|
50
|
+
# Creates a test double representing `string_or_model_class` with common
|
51
51
|
# ActiveModel methods stubbed out. Additional methods may be easily
|
52
|
-
# stubbed (via add_stubs) if
|
52
|
+
# stubbed (via add_stubs) if `stubs` is passed. This is most useful for
|
53
53
|
# impersonating models that don't exist yet.
|
54
54
|
#
|
55
55
|
# NOTE that only ActiveModel's methods, plus <tt>new_record?</tt>, are
|
@@ -59,7 +59,7 @@ module RSpec
|
|
59
59
|
# ActiveModel API (which declares <tt>persisted?</tt>, not
|
60
60
|
# <tt>new_record?</tt>).
|
61
61
|
#
|
62
|
-
#
|
62
|
+
# `string_or_model_class` can be any of:
|
63
63
|
#
|
64
64
|
# * A String representing a Class that does not exist
|
65
65
|
# * A String representing a Class that extends ActiveModel::Naming
|
@@ -90,20 +90,22 @@ EOM
|
|
90
90
|
end
|
91
91
|
|
92
92
|
stubs = stubs.reverse_merge(:id => next_id)
|
93
|
-
stubs = stubs.reverse_merge(:persisted? => !!stubs[:id]
|
94
|
-
|
95
|
-
|
96
|
-
|
93
|
+
stubs = stubs.reverse_merge(:persisted? => !!stubs[:id],
|
94
|
+
:destroyed? => false,
|
95
|
+
:marked_for_destruction? => false,
|
96
|
+
:blank? => false)
|
97
97
|
|
98
98
|
mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
|
99
|
-
m.
|
100
|
-
|
101
|
-
|
99
|
+
m.singleton_class.class_eval do
|
100
|
+
include ActiveModelInstanceMethods
|
101
|
+
include ActiveRecordInstanceMethods if defined?(ActiveRecord)
|
102
|
+
include ActiveModel::Conversion
|
103
|
+
include ActiveModel::Validations
|
104
|
+
end
|
102
105
|
if defined?(ActiveRecord)
|
103
|
-
m.extend ActiveRecordInstanceMethods
|
104
106
|
[:save, :update_attributes].each do |key|
|
105
107
|
if stubs[key] == false
|
106
|
-
m.errors.stub(:empty?
|
108
|
+
m.errors.stub(:empty? => false)
|
107
109
|
end
|
108
110
|
end
|
109
111
|
end
|
@@ -119,11 +121,19 @@ EOM
|
|
119
121
|
def @object.instance_of?(other)
|
120
122
|
other == #{model_class}
|
121
123
|
end unless #{stubs.has_key?(:instance_of?)}
|
122
|
-
|
124
|
+
|
125
|
+
def @object.__model_class_has_column?(method_name)
|
126
|
+
#{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s)
|
127
|
+
end
|
128
|
+
|
123
129
|
def @object.respond_to?(method_name, include_private=false)
|
124
|
-
|
130
|
+
__model_class_has_column?(method_name) ? true : super
|
125
131
|
end unless #{stubs.has_key?(:respond_to?)}
|
126
132
|
|
133
|
+
def @object.method_missing(m, *a, &b)
|
134
|
+
respond_to?(m) ? nil : super
|
135
|
+
end
|
136
|
+
|
127
137
|
def @object.class
|
128
138
|
#{model_class}
|
129
139
|
end unless #{stubs.has_key?(:class)}
|
@@ -137,27 +147,27 @@ EOM
|
|
137
147
|
end
|
138
148
|
|
139
149
|
module ActiveModelStubExtensions
|
140
|
-
# Stubs
|
150
|
+
# Stubs `persisted` to return false and `id` to return nil
|
141
151
|
def as_new_record
|
142
152
|
self.stub(:persisted?) { false }
|
143
153
|
self.stub(:id) { nil }
|
144
154
|
self
|
145
155
|
end
|
146
156
|
|
147
|
-
# Returns
|
157
|
+
# Returns `true` by default. Override with a stub.
|
148
158
|
def persisted?
|
149
159
|
true
|
150
160
|
end
|
151
161
|
end
|
152
162
|
|
153
163
|
module ActiveRecordStubExtensions
|
154
|
-
# Stubs
|
164
|
+
# Stubs `id` (or other primary key method) to return nil
|
155
165
|
def as_new_record
|
156
166
|
self.__send__("#{self.class.primary_key}=", nil)
|
157
167
|
super
|
158
168
|
end
|
159
169
|
|
160
|
-
# Returns the opposite of
|
170
|
+
# Returns the opposite of `persisted?`.
|
161
171
|
def new_record?
|
162
172
|
!persisted?
|
163
173
|
end
|
@@ -169,11 +179,11 @@ EOM
|
|
169
179
|
end
|
170
180
|
end
|
171
181
|
|
172
|
-
# Creates an instance of
|
173
|
-
# generated value that is unique to each object.. If
|
174
|
-
#
|
182
|
+
# Creates an instance of `Model` with `to_param` stubbed using a
|
183
|
+
# generated value that is unique to each object.. If `Model` is an
|
184
|
+
# `ActiveRecord` model, it is prohibited from accessing the database*.
|
175
185
|
#
|
176
|
-
# For each key in
|
186
|
+
# For each key in `hash_of_stubs`, if the model has a matching attribute
|
177
187
|
# (determined by asking it) are simply assigned the submitted values. If
|
178
188
|
# the model does not have a matching attribute, the key/value pair is
|
179
189
|
# assigned as a stub return value using RSpec's mocking/stubbing
|
@@ -181,23 +191,21 @@ EOM
|
|
181
191
|
#
|
182
192
|
# <tt>persisted?</tt> is overridden to return the result of !id.nil?
|
183
193
|
# This means that by default persisted? will return true. If you want
|
184
|
-
# the object to behave as a new record, sending it
|
194
|
+
# the object to behave as a new record, sending it `as_new_record` will
|
185
195
|
# set the id to nil. You can also explicitly set :id => nil, in which
|
186
|
-
# case persisted? will return false, but using
|
196
|
+
# case persisted? will return false, but using `as_new_record` makes the
|
187
197
|
# example a bit more descriptive.
|
188
198
|
#
|
189
199
|
# While you can use stub_model in any example (model, view, controller,
|
190
200
|
# helper), it is especially useful in view examples, which are
|
191
201
|
# inherently more state-based than interaction-based.
|
192
202
|
#
|
193
|
-
#
|
203
|
+
# @example
|
194
204
|
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
# person.first_name = "David"
|
200
|
-
# end
|
205
|
+
# stub_model(Person)
|
206
|
+
# stub_model(Person).as_new_record
|
207
|
+
# stub_model(Person, :to_param => 37)
|
208
|
+
# stub_model(Person) {|person| person.first_name = "David"}
|
201
209
|
def stub_model(model_class, stubs={})
|
202
210
|
model_class.new.tap do |m|
|
203
211
|
m.extend ActiveModelStubExtensions
|