rspec-rails 2.8.0.rc1 → 2.8.0.rc2

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.
Files changed (38) hide show
  1. data/License.txt +22 -0
  2. data/README.md +253 -119
  3. data/features/README.md +0 -2
  4. data/features/Upgrade.md +1 -1
  5. data/lib/generators/rspec/install/templates/spec/spec_helper.rb +1 -1
  6. data/lib/generators/rspec/integration/integration_generator.rb +1 -0
  7. data/lib/generators/rspec/scaffold/scaffold_generator.rb +1 -0
  8. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +23 -16
  9. data/lib/rspec/rails/adapters.rb +25 -13
  10. data/lib/rspec/rails/example/controller_example_group.rb +65 -122
  11. data/lib/rspec/rails/example/helper_example_group.rb +11 -36
  12. data/lib/rspec/rails/example/mailer_example_group.rb +1 -1
  13. data/lib/rspec/rails/example/rails_example_group.rb +5 -0
  14. data/lib/rspec/rails/example/request_example_group.rb +4 -19
  15. data/lib/rspec/rails/example/routing_example_group.rb +8 -10
  16. data/lib/rspec/rails/example/view_example_group.rb +21 -35
  17. data/lib/rspec/rails/extensions/active_record/base.rb +15 -12
  18. data/lib/rspec/rails/fixture_support.rb +1 -1
  19. data/lib/rspec/rails/matchers/be_a_new.rb +63 -30
  20. data/lib/rspec/rails/matchers/be_new_record.rb +18 -3
  21. data/lib/rspec/rails/matchers/have_extension.rb +26 -14
  22. data/lib/rspec/rails/matchers/redirect_to.rb +26 -7
  23. data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
  24. data/lib/rspec/rails/matchers/render_template.rb +27 -8
  25. data/lib/rspec/rails/matchers/routing_matchers.rb +72 -24
  26. data/lib/rspec/rails/mocks.rb +42 -34
  27. data/lib/rspec/rails/module_inclusion.rb +2 -1
  28. data/lib/rspec/rails/version.rb +1 -1
  29. data/lib/rspec/rails/view_assigns.rb +31 -34
  30. data/lib/rspec/rails/view_rendering.rb +10 -6
  31. data/spec/rspec/rails/example/controller_example_group_spec.rb +2 -2
  32. data/spec/rspec/rails/example/helper_example_group_spec.rb +5 -5
  33. data/spec/rspec/rails/example/view_example_group_spec.rb +5 -5
  34. data/spec/rspec/rails/matchers/be_a_new_spec.rb +2 -0
  35. data/spec/rspec/rails/matchers/be_new_record_spec.rb +2 -0
  36. data/spec/rspec/rails/matchers/render_template_spec.rb +3 -5
  37. data/spec/rspec/rails/mocks/mock_model_spec.rb +28 -0
  38. 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 Matchers
6
- class Have
7
- def failure_message_for_should_with_errors_on_extensions
8
- return "expected #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}, got #{@actual}" if @collection_name == :errors_on
9
- return "expected #{relativities[@relativity]}#{@expected} error on :#{@args[0]}, got #{@actual}" if @collection_name == :error_on
10
- return failure_message_for_should_without_errors_on_extensions
11
- end
12
- alias_method_chain :failure_message_for_should, :errors_on_extensions
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
- def description_with_errors_on_extensions
15
- return "have #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}" if @collection_name == :errors_on
16
- return "have #{relativities[@relativity]}#{@expected} error on :#{@args[0]}" if @collection_name == :error_on
17
- return description_without_errors_on_extensions
18
- end
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
- extend RSpec::Matchers::DSL
3
+ class RedirectTo
4
+ include RSpec::Matchers::BaseMatcher
4
5
 
5
- matcher :redirect_to do |destination|
6
- match_unless_raises ActiveSupport::TestCase::Assertion do |_|
7
- assert_redirected_to destination
6
+ def initialize(scope, expected)
7
+ super(expected)
8
+ @scope = scope
8
9
  end
9
10
 
10
- failure_message_for_should do |_|
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
- failure_message_for_should_not do |_|
15
- "expected not to redirect to #{destination.inspect}, but did"
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,3 +1,3 @@
1
- if defined?(ActiveRecord)
1
+ if defined?(ActiveRecord::Relation)
2
2
  RSpec::Matchers::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::MatchArray)
3
3
  end
@@ -1,20 +1,39 @@
1
1
  module RSpec::Rails::Matchers
2
2
  module RenderTemplate
3
- extend RSpec::Matchers::DSL
3
+ class RenderTemplateMatcher
4
+ include RSpec::Matchers::BaseMatcher
4
5
 
5
- matcher :render_template do |options, message|
6
- match_unless_raises ActiveSupport::TestCase::Assertion do |_|
7
- options = options.to_s if Symbol === options
8
- assert_template options, message
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
- failure_message_for_should do
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
- failure_message_for_should_not do |_|
16
- "expected not to render #{options.inspect}, but did"
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
- matcher :route_to do |*expected|
6
- expected_options = expected[1] || {}
7
- if Hash === expected[0]
8
- expected_options.merge!(expected[0])
9
- else
10
- controller, action = expected[0].split('#')
11
- expected_options.merge!(:controller => controller, :action => action)
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
- match_unless_raises ActiveSupport::TestCase::Assertion do |verb_to_path_map|
15
- path, query = *verb_to_path_map.values.first.split('?')
16
- assert_recognizes(
17
- expected_options,
18
- {:method => verb_to_path_map.keys.first, :path => path},
19
- Rack::Utils::parse_query(query)
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
- failure_message_for_should do
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
- matcher :be_routable do
30
- match_unless_raises ActionController::RoutingError do |path|
31
- @routing_options = routes.recognize_path(
32
- path.values.first, :method => path.keys.first
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
- failure_message_for_should_not do |path|
37
- "expected #{path.inspect} not to be routable, but it routes to #{@routing_options.inspect}"
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
- module RouteHelpers
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
@@ -9,7 +9,7 @@ module RSpec
9
9
  module Mocks
10
10
 
11
11
  module ActiveModelInstanceMethods
12
- # Stubs +persisted?+ to return false and +id+ to return nil
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 +persisted?+ to return +false+ and +id+ to return +nil+.
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 +persisted?+
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 +string_or_model_class+ with common
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 +stubs+ is passed. This is most useful for
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
- # +string_or_model_class+ can be any of:
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
- stubs = stubs.reverse_merge(:destroyed? => false)
95
- stubs = stubs.reverse_merge(:marked_for_destruction? => false)
96
- stubs = stubs.reverse_merge(:blank? => false)
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.extend ActiveModelInstanceMethods
100
- m.singleton_class.__send__ :include, ActiveModel::Conversion
101
- m.singleton_class.__send__ :include, ActiveModel::Validations
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?) { false }
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
- #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super
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 +persisted+ to return false and +id+ to return nil
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 +true+ by default. Override with a stub.
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 +id+ (or other primary key method) to return nil
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 +persisted?+.
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 +Model+ with +to_param+ stubbed using a
173
- # generated value that is unique to each object.. If +Model+ is an
174
- # +ActiveRecord+ model, it is prohibited from accessing the database*.
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 +hash_of_stubs+, if the model has a matching attribute
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 +as_new_record+ will
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 +as_new_record+ makes the
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
- # == Examples
203
+ # @example
194
204
  #
195
- # stub_model(Person)
196
- # stub_model(Person).as_new_record
197
- # stub_model(Person, :to_param => 37)
198
- # stub_model(Person) do |person|
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
@@ -1,6 +1,7 @@
1
1
  module RSpec::Rails
2
2
  module ModuleInclusion
3
- # Deprecated as of rspec-rails-2.4
3
+ # @deprecated No replacement.
4
+ #
4
5
  # Will be removed from rspec-rails-3.0
5
6
  #
6
7
  # This was never intended to be a public API and is no longer needed