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.
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