rspec-rails 2.0.0.beta.8 → 2.0.0.beta.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/README.markdown +82 -27
  2. data/Rakefile +7 -7
  3. data/Upgrade.markdown +12 -0
  4. data/VERSION +1 -1
  5. data/autotest/discover.rb +1 -0
  6. data/cucumber.yml +3 -0
  7. data/example_app_template.rb +2 -2
  8. data/features/controller_specs/do_not_render_views.feature +61 -0
  9. data/features/controller_specs/readers.feature +18 -0
  10. data/features/controller_specs/render_views.feature +53 -0
  11. data/features/model_specs/errors_on.feature +32 -0
  12. data/features/model_specs/transactional_examples.feature +2 -2
  13. data/features/view_specs/view_spec.feature +102 -1
  14. data/lib/autotest/rails_rspec2.rb +1 -1
  15. data/lib/generators/rspec/helper/helper_generator.rb +1 -0
  16. data/lib/generators/rspec/helper/templates/helper_spec.rb +3 -3
  17. data/lib/generators/rspec/install/install_generator.rb +7 -7
  18. data/lib/generators/rspec/scaffold/scaffold_generator.rb +10 -1
  19. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +1 -1
  20. data/lib/generators/rspec/scaffold/templates/index_spec.rb +1 -1
  21. data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
  22. data/lib/generators/rspec/scaffold/templates/show_spec.rb +2 -2
  23. data/lib/rspec-rails.rb +12 -0
  24. data/lib/rspec/rails.rb +8 -0
  25. data/lib/rspec/rails/adapters.rb +12 -8
  26. data/lib/rspec/rails/example.rb +1 -0
  27. data/lib/rspec/rails/example/controller_example_group.rb +13 -12
  28. data/lib/rspec/rails/example/helper_example_group.rb +43 -0
  29. data/lib/rspec/rails/example/mailer_example_group.rb +10 -5
  30. data/lib/rspec/rails/example/request_example_group.rb +7 -7
  31. data/lib/rspec/rails/example/view_example_group.rb +44 -62
  32. data/lib/rspec/rails/extensions.rb +1 -0
  33. data/lib/rspec/rails/extensions/active_record/base.rb +46 -0
  34. data/lib/rspec/rails/matchers.rb +28 -3
  35. data/lib/rspec/rails/mocks.rb +3 -3
  36. data/lib/rspec/rails/monkey.rb +1 -0
  37. data/lib/rspec/rails/monkey/action_controller/test_case.rb +153 -145
  38. data/lib/rspec/rails/monkey/action_view/test_case.rb +201 -0
  39. data/lib/rspec/rails/null_resolver.rb +10 -0
  40. data/lib/{generators/rspec/install/templates/lib → rspec/rails}/tasks/rspec.rake +4 -5
  41. data/lib/rspec/rails/transactional_database_support.rb +4 -6
  42. data/lib/rspec/rails/version.rb +1 -1
  43. data/lib/rspec/rails/view_assigns.rb +28 -0
  44. data/lib/rspec/rails/view_rendering.rb +33 -0
  45. data/rspec-rails.gemspec +41 -11
  46. data/spec/rspec/rails/example/controller_example_group_spec.rb +11 -0
  47. data/spec/rspec/rails/example/helper_example_group_spec.rb +20 -0
  48. data/spec/rspec/rails/example/mailer_example_group_spec.rb +11 -0
  49. data/spec/rspec/rails/example/request_example_group_spec.rb +11 -0
  50. data/spec/rspec/rails/example/view_example_group_spec.rb +75 -0
  51. data/spec/rspec/rails/example/view_rendering_spec.rb +68 -0
  52. data/spec/rspec/rails/matchers/errors_on_spec.rb +38 -0
  53. data/spec/rspec/rails/mocks/ar_classes.rb +1 -1
  54. data/spec/rspec/rails/mocks/mock_model_spec.rb +88 -65
  55. data/spec/rspec/rails/mocks/stub_model_spec.rb +1 -1
  56. data/spec/rspec/rails/transactional_database_support_spec.rb +2 -2
  57. data/spec/spec_helper.rb +18 -2
  58. data/spec/support/helpers.rb +20 -0
  59. data/specs.watchr +10 -10
  60. data/templates/generate_stuff.rb +3 -1
  61. metadata +40 -9
@@ -0,0 +1 @@
1
+ require 'rspec/rails/extensions/active_record/base'
@@ -0,0 +1,46 @@
1
+ if defined?(ActiveRecord::Base)
2
+ module RSpec
3
+ module Rails
4
+ module Extensions
5
+ module ActiveRecord
6
+ module ClassMethods
7
+ # :call-seq:
8
+ # ModelClass.should have(:no).records
9
+ # ModelClass.should have(1).record
10
+ # ModelClass.should have(n).records
11
+ #
12
+ # Extension to enhance <tt>should have</tt> on AR Model classes
13
+ def records
14
+ find(:all)
15
+ end
16
+ alias :record :records
17
+ end
18
+
19
+ module InstanceMethods
20
+ # :call-seq:
21
+ # model.should have(:no).errors_on(:attribute)
22
+ # model.should have(1).error_on(:attribute)
23
+ # model.should have(n).errors_on(:attribute)
24
+ #
25
+ # Extension to enhance <tt>should have</tt> on AR Model instances.
26
+ # Calls model.valid? in order to prepare the object's errors
27
+ # object.
28
+ def errors_on(attribute)
29
+ self.valid?
30
+ [self.errors[attribute]].flatten.compact
31
+ end
32
+ alias :error_on :errors_on
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module ActiveRecord #:nodoc:
40
+ class Base
41
+ extend RSpec::Rails::Extensions::ActiveRecord::ClassMethods
42
+ include RSpec::Rails::Extensions::ActiveRecord::InstanceMethods
43
+ end
44
+ end
45
+ end
46
+
@@ -17,21 +17,46 @@ rescue LoadError
17
17
 
18
18
  end
19
19
 
20
- Rspec::Matchers.define :redirect_to do |destination|
20
+ RSpec::Matchers.define :redirect_to do |destination|
21
21
  match_unless_raises Test::Unit::AssertionFailedError do |_|
22
22
  assert_redirected_to destination
23
23
  end
24
24
  end
25
25
 
26
- Rspec::Matchers.define :render_template do |options, message|
26
+ RSpec::Matchers.define :render_template do |options, message|
27
27
  match_unless_raises Test::Unit::AssertionFailedError do |_|
28
28
  options = options.to_s if Symbol === options
29
29
  assert_template options, message
30
30
  end
31
31
  end
32
32
 
33
- Rspec::Matchers.define :be_a_new do |model_klass|
33
+ RSpec::Matchers.define :be_a_new do |model_klass|
34
34
  match do |actual|
35
35
  model_klass === actual && actual.new_record?
36
36
  end
37
37
  end
38
+
39
+ require 'rspec/matchers/have'
40
+
41
+ module RSpec #:nodoc:
42
+ module Matchers #:nodoc:
43
+ class Have #:nodoc:
44
+
45
+ def failure_message_for_should_with_errors_on_extensions
46
+ return "expected #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}, got #{@actual}" if @collection_name == :errors_on
47
+ return "expected #{relativities[@relativity]}#{@expected} error on :#{@args[0]}, got #{@actual}" if @collection_name == :error_on
48
+ return failure_message_for_should_without_errors_on_extensions
49
+ end
50
+ alias_method_chain :failure_message_for_should, :errors_on_extensions
51
+
52
+ def description_with_errors_on_extensions
53
+ return "have #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}" if @collection_name == :errors_on
54
+ return "have #{relativities[@relativity]}#{@expected} error on :#{@args[0]}" if @collection_name == :error_on
55
+ return description_without_errors_on_extensions
56
+ end
57
+ alias_method_chain :description, :errors_on_extensions
58
+
59
+ end
60
+ end
61
+ end
62
+
@@ -1,4 +1,4 @@
1
- module Rspec
1
+ module RSpec
2
2
  module Rails
3
3
 
4
4
  class IllegalDataAccessException < StandardError; end
@@ -140,6 +140,6 @@ module Rspec
140
140
  end
141
141
  end
142
142
 
143
- Rspec.configure do |c|
144
- c.include Rspec::Rails::Mocks
143
+ RSpec.configure do |c|
144
+ c.include RSpec::Rails::Mocks
145
145
  end
@@ -1,2 +1,3 @@
1
1
  require 'rspec/rails/monkey/action_controller/test_case'
2
+ require 'rspec/rails/monkey/action_view/test_case'
2
3
  require 'rspec/rails/monkey/active_support/notifications/fanout'
@@ -7,193 +7,201 @@ module ActionController
7
7
  # Once 3.0.0.rc.1 comes out, we can remove it.
8
8
  module TemplateAssertions
9
9
  def teardown_subscriptions
10
+ # rails-3.0.0.beta.3
10
11
  ActiveSupport::Notifications.unsubscribe("action_view.render_template")
11
12
  ActiveSupport::Notifications.unsubscribe("action_view.render_template!")
13
+
14
+ # as of 731d4392e478ff5526b595074d9caa999da8bd0c
15
+ ActiveSupport::Notifications.unsubscribe("render_template.action_view")
16
+ ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
12
17
  end
13
18
  end
14
19
 
15
- # The remainder of this file has yet to be merged to rails HEAD and is
16
- # therefore merely speculative and hopeful.
17
- class TestCase < ActiveSupport::TestCase
18
- module Behavior
19
- extend ActiveSupport::Concern
20
- include ActionDispatch::TestProcess
20
+ # This has been merged to rails HEAD after the 3.0.0.beta.3 release (see
21
+ # https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4474).
22
+ # Once 3.0.0.rc.1 comes out, we can remove it.
23
+ unless defined?(ActionController::TestCase::Behavior)
24
+ class TestCase < ActiveSupport::TestCase
25
+ module Behavior
26
+ extend ActiveSupport::Concern
27
+ include ActionDispatch::TestProcess
28
+
29
+ attr_reader :response, :request
30
+
31
+ module ClassMethods
32
+
33
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
34
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
35
+ def tests(controller_class)
36
+ self.controller_class = controller_class
37
+ end
38
+
39
+ def controller_class=(new_class)
40
+ prepare_controller_class(new_class) if new_class
41
+ write_inheritable_attribute(:controller_class, new_class)
42
+ end
43
+
44
+ def controller_class
45
+ if current_controller_class = read_inheritable_attribute(:controller_class)
46
+ current_controller_class
47
+ else
48
+ self.controller_class = determine_default_controller_class(name)
49
+ end
50
+ end
21
51
 
22
- attr_reader :response, :request
52
+ def determine_default_controller_class(name)
53
+ name.sub(/Test$/, '').constantize
54
+ rescue NameError
55
+ nil
56
+ end
23
57
 
24
- module ClassMethods
58
+ def prepare_controller_class(new_class)
59
+ new_class.send :include, ActionController::TestCase::RaiseActionExceptions
60
+ end
25
61
 
26
- # Sets the controller class name. Useful if the name can't be inferred from test class.
27
- # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
28
- def tests(controller_class)
29
- self.controller_class = controller_class
30
62
  end
31
-
32
- def controller_class=(new_class)
33
- prepare_controller_class(new_class) if new_class
34
- write_inheritable_attribute(:controller_class, new_class)
63
+
64
+ # Executes a request simulating GET HTTP method and set/volley the response
65
+ def get(action, parameters = nil, session = nil, flash = nil)
66
+ process(action, parameters, session, flash, "GET")
35
67
  end
36
68
 
37
- def controller_class
38
- if current_controller_class = read_inheritable_attribute(:controller_class)
39
- current_controller_class
40
- else
41
- self.controller_class = determine_default_controller_class(name)
42
- end
69
+ # Executes a request simulating POST HTTP method and set/volley the response
70
+ def post(action, parameters = nil, session = nil, flash = nil)
71
+ process(action, parameters, session, flash, "POST")
43
72
  end
44
73
 
45
- def determine_default_controller_class(name)
46
- name.sub(/Test$/, '').constantize
47
- rescue NameError
48
- nil
74
+ # Executes a request simulating PUT HTTP method and set/volley the response
75
+ def put(action, parameters = nil, session = nil, flash = nil)
76
+ process(action, parameters, session, flash, "PUT")
49
77
  end
50
78
 
51
- def prepare_controller_class(new_class)
52
- new_class.send :include, ActionController::TestCase::RaiseActionExceptions
79
+ # Executes a request simulating DELETE HTTP method and set/volley the response
80
+ def delete(action, parameters = nil, session = nil, flash = nil)
81
+ process(action, parameters, session, flash, "DELETE")
53
82
  end
54
83
 
55
- end
84
+ # Executes a request simulating HEAD HTTP method and set/volley the response
85
+ def head(action, parameters = nil, session = nil, flash = nil)
86
+ process(action, parameters, session, flash, "HEAD")
87
+ end
56
88
 
57
- # Executes a request simulating GET HTTP method and set/volley the response
58
- def get(action, parameters = nil, session = nil, flash = nil)
59
- process(action, parameters, session, flash, "GET")
60
- end
89
+ def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
90
+ @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
91
+ @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
92
+ returning __send__(request_method, action, parameters, session, flash) do
93
+ @request.env.delete 'HTTP_X_REQUESTED_WITH'
94
+ @request.env.delete 'HTTP_ACCEPT'
95
+ end
96
+ end
97
+ alias xhr :xml_http_request
98
+
99
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
100
+ # Sanity check for required instance variables so we can give an
101
+ # understandable error message.
102
+ %w(@routes @controller @request @response).each do |iv_name|
103
+ if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
104
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
105
+ end
106
+ end
61
107
 
62
- # Executes a request simulating POST HTTP method and set/volley the response
63
- def post(action, parameters = nil, session = nil, flash = nil)
64
- process(action, parameters, session, flash, "POST")
65
- end
108
+ @request.recycle!
109
+ @response.recycle!
110
+ @controller.response_body = nil
111
+ @controller.formats = nil
112
+ @controller.params = nil
66
113
 
67
- # Executes a request simulating PUT HTTP method and set/volley the response
68
- def put(action, parameters = nil, session = nil, flash = nil)
69
- process(action, parameters, session, flash, "PUT")
70
- end
114
+ @html_document = nil
115
+ @request.env['REQUEST_METHOD'] = http_method
71
116
 
72
- # Executes a request simulating DELETE HTTP method and set/volley the response
73
- def delete(action, parameters = nil, session = nil, flash = nil)
74
- process(action, parameters, session, flash, "DELETE")
75
- end
117
+ parameters ||= {}
118
+ @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
76
119
 
77
- # Executes a request simulating HEAD HTTP method and set/volley the response
78
- def head(action, parameters = nil, session = nil, flash = nil)
79
- process(action, parameters, session, flash, "HEAD")
80
- end
120
+ @request.session = ActionController::TestSession.new(session) unless session.nil?
121
+ @request.session["flash"] = @request.flash.update(flash || {})
122
+ @request.session["flash"].sweep
81
123
 
82
- def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
83
- @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
84
- @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
85
- returning __send__(request_method, action, parameters, session, flash) do
86
- @request.env.delete 'HTTP_X_REQUESTED_WITH'
87
- @request.env.delete 'HTTP_ACCEPT'
88
- end
89
- end
90
- alias xhr :xml_http_request
91
-
92
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
93
- # Sanity check for required instance variables so we can give an
94
- # understandable error message.
95
- %w(@routes @controller @request @response).each do |iv_name|
96
- if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
97
- raise "#{iv_name} is nil: make sure you set it in your test's setup method."
98
- end
124
+ @controller.request = @request
125
+ @controller.params.merge!(parameters)
126
+ build_request_uri(action, parameters)
127
+ Base.class_eval { include Testing }
128
+ @controller.process_with_new_base_test(@request, @response)
129
+ @request.session.delete('flash') if @request.session['flash'].blank?
130
+ @response
99
131
  end
100
132
 
101
- @request.recycle!
102
- @response.recycle!
103
- @controller.response_body = nil
104
- @controller.formats = nil
105
- @controller.params = nil
106
-
107
- @html_document = nil
108
- @request.env['REQUEST_METHOD'] = http_method
109
-
110
- parameters ||= {}
111
- @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
112
-
113
- @request.session = ActionController::TestSession.new(session) unless session.nil?
114
- @request.session["flash"] = @request.flash.update(flash || {})
115
- @request.session["flash"].sweep
116
-
117
- @controller.request = @request
118
- @controller.params.merge!(parameters)
119
- build_request_uri(action, parameters)
120
- Base.class_eval { include Testing }
121
- @controller.process_with_new_base_test(@request, @response)
122
- @request.session.delete('flash') if @request.session['flash'].blank?
123
- @response
124
- end
133
+ def setup_controller_request_and_response
134
+ @request = TestRequest.new
135
+ @response = TestResponse.new
125
136
 
126
- def setup_controller_request_and_response
127
- @request = TestRequest.new
128
- @response = TestResponse.new
129
-
130
- if klass = self.class.controller_class
131
- @controller ||= klass.new rescue nil
132
- end
137
+ if klass = self.class.controller_class
138
+ @controller ||= klass.new rescue nil
139
+ end
133
140
 
134
- @request.env.delete('PATH_INFO')
141
+ @request.env.delete('PATH_INFO')
135
142
 
136
- if @controller
137
- @controller.request = @request
138
- @controller.params = {}
143
+ if @controller
144
+ @controller.request = @request
145
+ @controller.params = {}
146
+ end
139
147
  end
140
- end
141
148
 
142
- # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
143
- def rescue_action_in_public!
144
- @request.remote_addr = '208.77.188.166' # example.com
145
- end
149
+ # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
150
+ def rescue_action_in_public!
151
+ @request.remote_addr = '208.77.188.166' # example.com
152
+ end
146
153
 
147
- included do
148
- include ActionController::TemplateAssertions
149
- include ActionDispatch::Assertions
150
- setup :setup_controller_request_and_response
151
- end
154
+ included do
155
+ include ActionController::TemplateAssertions
156
+ include ActionDispatch::Assertions
157
+ setup :setup_controller_request_and_response
158
+ end
152
159
 
153
- private
160
+ private
154
161
 
155
- def build_request_uri(action, parameters)
156
- unless @request.env["PATH_INFO"]
157
- options = @controller.__send__(:url_options).merge(parameters)
158
- options.update(
159
- :only_path => true,
160
- :action => action,
161
- :relative_url_root => nil,
162
- :_path_segments => @request.symbolized_path_parameters)
162
+ def build_request_uri(action, parameters)
163
+ unless @request.env["PATH_INFO"]
164
+ options = @controller.__send__(:url_options).merge(parameters)
165
+ options.update(
166
+ :only_path => true,
167
+ :action => action,
168
+ :relative_url_root => nil,
169
+ :_path_segments => @request.symbolized_path_parameters)
163
170
 
164
- url, query_string = @routes.url_for(options).split("?", 2)
171
+ url, query_string = @routes.url_for(options).split("?", 2)
165
172
 
166
- @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
167
- @request.env["PATH_INFO"] = url
168
- @request.env["QUERY_STRING"] = query_string || ""
173
+ @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
174
+ @request.env["PATH_INFO"] = url
175
+ @request.env["QUERY_STRING"] = query_string || ""
176
+ end
169
177
  end
170
178
  end
171
- end
172
179
 
173
- # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
174
- # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
175
- # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
176
- # than 0.0.0.0.
177
- #
178
- # The exception is stored in the exception accessor for further inspection.
179
- module RaiseActionExceptions
180
- def self.included(base)
181
- base.class_eval do
182
- attr_accessor :exception
183
- protected :exception, :exception=
180
+ # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
181
+ # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
182
+ # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
183
+ # than 0.0.0.0.
184
+ #
185
+ # The exception is stored in the exception accessor for further inspection.
186
+ module RaiseActionExceptions
187
+ def self.included(base)
188
+ base.class_eval do
189
+ attr_accessor :exception
190
+ protected :exception, :exception=
191
+ end
184
192
  end
185
- end
186
193
 
187
- protected
188
- def rescue_action_without_handler(e)
189
- self.exception = e
194
+ protected
195
+ def rescue_action_without_handler(e)
196
+ self.exception = e
190
197
 
191
- if request.remote_addr == "0.0.0.0"
192
- raise(e)
193
- else
194
- super(e)
198
+ if request.remote_addr == "0.0.0.0"
199
+ raise(e)
200
+ else
201
+ super(e)
202
+ end
195
203
  end
196
- end
204
+ end
197
205
  end
198
206
  end
199
207
  end