rspec-rails 2.0.0.beta.22 → 2.6.0

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 (134) hide show
  1. data/.gitignore +10 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +10 -0
  4. data/History.md +1 -33
  5. data/License.txt +23 -0
  6. data/{README.markdown → README.md} +27 -7
  7. data/README_DEV.md +43 -0
  8. data/Rakefile +105 -35
  9. data/Thorfile +40 -0
  10. data/Upgrade.md +1 -0
  11. data/features/.nav +34 -0
  12. data/features/Autotest.md +24 -0
  13. data/features/Changelog.md +151 -0
  14. data/features/Generators.md +8 -0
  15. data/features/GettingStarted.md +84 -0
  16. data/features/README.md +58 -0
  17. data/features/Upgrade.md +117 -0
  18. data/features/controller_specs/README.md +45 -0
  19. data/features/controller_specs/anonymous_controller.feature +7 -7
  20. data/features/controller_specs/controller_spec.feature +18 -0
  21. data/features/controller_specs/isolation_from_views.feature +55 -8
  22. data/features/controller_specs/render_views.feature +63 -52
  23. data/features/helper_specs/helper_spec.feature +40 -6
  24. data/features/mailer_specs/url_helpers.feature +4 -4
  25. data/features/matchers/README.md +4 -0
  26. data/features/matchers/new_record_matcher.feature +6 -48
  27. data/features/matchers/redirect_to_matcher.feature +5 -6
  28. data/features/matchers/render_template_matcher.feature +13 -10
  29. data/features/mocks/mock_model.feature +29 -13
  30. data/features/mocks/stub_model.feature +4 -4
  31. data/features/model_specs/README.md +21 -0
  32. data/features/model_specs/errors_on.feature +2 -2
  33. data/features/model_specs/transactional_examples.feature +14 -9
  34. data/features/request_specs/request_spec.feature +49 -0
  35. data/features/routing_specs/README.md +17 -0
  36. data/features/{matchers → routing_specs}/be_routable_matcher.feature +25 -25
  37. data/features/routing_specs/named_routes.feature +18 -0
  38. data/features/routing_specs/route_to_matcher.feature +58 -0
  39. data/features/step_definitions/additional_cli_steps.rb +4 -0
  40. data/features/support/env.rb +14 -6
  41. data/features/view_specs/inferred_controller_path.feature +6 -6
  42. data/features/view_specs/stub_template.feature +51 -0
  43. data/features/view_specs/view_spec.feature +23 -42
  44. data/gemfiles/.bundle/config +2 -0
  45. data/gemfiles/base.rb +58 -0
  46. data/gemfiles/rails-3-0-stable +6 -0
  47. data/gemfiles/rails-3.0.0 +5 -0
  48. data/gemfiles/rails-3.0.1 +5 -0
  49. data/gemfiles/rails-3.0.2 +5 -0
  50. data/gemfiles/rails-3.0.3 +5 -0
  51. data/gemfiles/rails-3.0.4 +5 -0
  52. data/gemfiles/rails-3.0.5 +5 -0
  53. data/gemfiles/rails-3.0.6 +5 -0
  54. data/gemfiles/rails-3.0.7 +5 -0
  55. data/gemfiles/rails-3.1.0.beta1 +5 -0
  56. data/gemfiles/rails-master +7 -0
  57. data/lib/autotest/rails_rspec2.rb +1 -1
  58. data/lib/generators/rspec/controller/controller_generator.rb +4 -4
  59. data/lib/generators/rspec/helper/helper_generator.rb +3 -3
  60. data/lib/generators/rspec/install/install_generator.rb +0 -8
  61. data/lib/generators/rspec/integration/integration_generator.rb +15 -1
  62. data/lib/generators/rspec/integration/templates/request_spec.rb +7 -1
  63. data/lib/generators/rspec/mailer/mailer_generator.rb +2 -2
  64. data/lib/generators/rspec/model/model_generator.rb +1 -1
  65. data/lib/generators/rspec/observer/observer_generator.rb +1 -1
  66. data/lib/generators/rspec/scaffold/scaffold_generator.rb +40 -11
  67. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +90 -58
  68. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +15 -7
  69. data/lib/generators/rspec/scaffold/templates/index_spec.rb +7 -2
  70. data/lib/generators/rspec/scaffold/templates/new_spec.rb +14 -7
  71. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +14 -14
  72. data/lib/generators/rspec/scaffold/templates/show_spec.rb +7 -2
  73. data/lib/generators/rspec/view/view_generator.rb +1 -1
  74. data/lib/rspec/rails/example/controller_example_group.rb +25 -34
  75. data/lib/rspec/rails/example/helper_example_group.rb +5 -11
  76. data/lib/rspec/rails/example/mailer_example_group.rb +0 -14
  77. data/lib/rspec/rails/example/model_example_group.rb +0 -4
  78. data/lib/rspec/rails/example/rails_example_group.rb +0 -2
  79. data/lib/rspec/rails/example/request_example_group.rb +5 -30
  80. data/lib/rspec/rails/example/routing_example_group.rb +3 -5
  81. data/lib/rspec/rails/example/view_example_group.rb +20 -11
  82. data/lib/rspec/rails/example.rb +31 -1
  83. data/lib/rspec/rails/extensions/active_record/base.rb +28 -32
  84. data/lib/rspec/rails/fixture_support.rb +1 -1
  85. data/lib/rspec/rails/matchers/be_new_record.rb +5 -0
  86. data/lib/rspec/rails/matchers/redirect_to.rb +6 -2
  87. data/lib/rspec/rails/matchers/render_template.rb +5 -1
  88. data/lib/rspec/rails/matchers/routing_matchers.rb +24 -3
  89. data/lib/rspec/rails/matchers.rb +1 -1
  90. data/lib/rspec/rails/mocks.rb +118 -79
  91. data/lib/rspec/rails/module_inclusion.rb +17 -0
  92. data/lib/rspec/rails/tasks/rspec.rake +1 -1
  93. data/lib/rspec/rails/vendor/capybara.rb +29 -0
  94. data/lib/rspec/rails/vendor/webrat.rb +33 -0
  95. data/lib/rspec/rails/version.rb +1 -1
  96. data/lib/rspec/rails/view_assigns.rb +23 -4
  97. data/lib/rspec/rails/view_rendering.rb +50 -13
  98. data/lib/rspec/rails.rb +7 -10
  99. data/lib/rspec-rails.rb +4 -2
  100. data/rspec-rails.gemspec +11 -39
  101. data/spec/autotest/rails_rspec2_spec.rb +25 -14
  102. data/spec/rspec/rails/configuration_spec.rb +26 -0
  103. data/spec/rspec/rails/deprecations_spec.rb +18 -0
  104. data/spec/rspec/rails/example/controller_example_group_spec.rb +47 -5
  105. data/spec/rspec/rails/example/helper_example_group_spec.rb +19 -1
  106. data/spec/rspec/rails/example/request_example_group_spec.rb +2 -0
  107. data/spec/rspec/rails/extensions/active_model/errors_on_spec.rb +23 -0
  108. data/spec/rspec/rails/extensions/active_record/records_spec.rb +9 -0
  109. data/spec/rspec/rails/matchers/be_new_record_spec.rb +17 -0
  110. data/spec/rspec/rails/matchers/be_routable_spec.rb +41 -0
  111. data/spec/rspec/rails/matchers/redirect_to_spec.rb +67 -8
  112. data/spec/rspec/rails/matchers/render_template_spec.rb +67 -10
  113. data/spec/rspec/rails/matchers/route_to_spec.rb +77 -31
  114. data/spec/rspec/rails/mocks/ar_classes.rb +17 -25
  115. data/spec/rspec/rails/mocks/mock_model_spec.rb +50 -13
  116. data/spec/rspec/rails/mocks/stub_model_spec.rb +121 -67
  117. data/spec/rspec/rails/view_rendering_spec.rb +105 -0
  118. data/spec/spec_helper.rb +0 -22
  119. data/spec/support/matchers.rb +9 -0
  120. data/templates/generate_stuff.rb +10 -2
  121. data/templates/run_specs.rb +2 -2
  122. metadata +141 -79
  123. data/Gemfile +0 -24
  124. data/Gotchas.markdown +0 -14
  125. data/Upgrade.markdown +0 -31
  126. data/autotest/discover.rb +0 -1
  127. data/features/README.markdown +0 -12
  128. data/features/controller_specs/readers.feature +0 -18
  129. data/features/routing_specs/access_to_named_routes.feature +0 -15
  130. data/lib/generators/rspec/install/templates/autotest/discover.rb +0 -2
  131. data/lib/rspec/rails/browser_simulators.rb +0 -30
  132. data/spec/rspec/rails/example/view_rendering_spec.rb +0 -110
  133. data/specs.watchr +0 -59
  134. data/templates/Gemfile +0 -18
@@ -13,51 +13,26 @@ module RSpec::Rails
13
13
  # => delegates to assert_redirected_to(destination)
14
14
  module RequestExampleGroup
15
15
  extend ActiveSupport::Concern
16
- extend RSpec::Rails::ModuleInclusion
17
-
18
16
  include RSpec::Rails::RailsExampleGroup
19
-
20
17
  include ActionDispatch::Integration::Runner
21
18
  include ActionDispatch::Assertions
22
- include RSpec::Rails::BrowserSimulators
23
-
24
- webrat do
25
- include Webrat::Matchers
26
- include Webrat::Methods
27
- end
28
-
29
- capybara do
30
- include Capybara
31
- end
32
-
33
- include RSpec::Rails::Matchers::RedirectTo
34
- include RSpec::Rails::Matchers::RenderTemplate
35
- include ActionController::TemplateAssertions
36
19
 
37
20
  module InstanceMethods
38
21
  def app
39
22
  ::Rails.application
40
23
  end
41
-
42
- def last_response
43
- response
44
- end
45
24
  end
46
25
 
26
+ include RSpec::Rails::Matchers::RedirectTo
27
+ include RSpec::Rails::Matchers::RenderTemplate
28
+ include ActionController::TemplateAssertions
29
+
47
30
  included do
48
31
  metadata[:type] = :request
49
32
 
50
33
  before do
51
- @router = ::Rails.application.routes
52
- end
53
-
54
- webrat do
55
- Webrat.configure do |config|
56
- config.mode = :rack
57
- end
34
+ @routes = ::Rails.application.routes
58
35
  end
59
36
  end
60
-
61
- RSpec.configure &include_self_when_dir_matches('spec','requests')
62
37
  end
63
38
  end
@@ -3,16 +3,16 @@ require "action_dispatch/testing/assertions/routing"
3
3
  module RSpec::Rails
4
4
  module RoutingExampleGroup
5
5
  extend ActiveSupport::Concern
6
- extend RSpec::Rails::ModuleInclusion
7
-
8
6
  include RSpec::Rails::RailsExampleGroup
9
-
10
7
  include ActionDispatch::Assertions::RoutingAssertions
11
8
  include RSpec::Rails::Matchers::RoutingMatchers
9
+ include RSpec::Rails::Matchers::RoutingMatchers::RouteHelpers
12
10
 
13
11
  module InstanceMethods
14
12
  attr_reader :routes
15
13
 
14
+ private
15
+
16
16
  def method_missing(m, *args, &block)
17
17
  routes.url_helpers.respond_to?(m) ? routes.url_helpers.send(m, *args) : super
18
18
  end
@@ -25,7 +25,5 @@ module RSpec::Rails
25
25
  @routes = ::Rails.application.routes
26
26
  end
27
27
  end
28
-
29
- RSpec.configure &include_self_when_dir_matches('spec','routing')
30
28
  end
31
29
  end
@@ -19,21 +19,14 @@ module RSpec::Rails
19
19
  # end
20
20
  module ViewExampleGroup
21
21
  extend ActiveSupport::Concern
22
- extend RSpec::Rails::ModuleInclusion
23
-
24
22
  include RSpec::Rails::RailsExampleGroup
25
23
  include ActionView::TestCase::Behavior
26
24
  include RSpec::Rails::ViewAssigns
27
25
  include RSpec::Rails::Matchers::RenderTemplate
28
- include RSpec::Rails::BrowserSimulators
29
-
30
- webrat do
31
- include Webrat::Matchers
32
- end
33
26
 
34
27
  module ClassMethods
35
28
  def _default_helper
36
- base = metadata[:behaviour][:description].split('/').first
29
+ base = metadata[:example_group][:description].split('/').first
37
30
  (base.camelize + 'Helper').constantize if base
38
31
  rescue NameError
39
32
  nil
@@ -85,6 +78,18 @@ module RSpec::Rails
85
78
  _view
86
79
  end
87
80
 
81
+ # Simulates the presence of a template on the file system by adding a
82
+ # Rails' FixtureResolver to the front of the view_paths list. Designed to
83
+ # help isolate view examples from partials rendered by the view template
84
+ # that is the subject of the example.
85
+ #
86
+ # == Example
87
+ #
88
+ # stub_template("widgets/_widget.html.erb" => "This content.")
89
+ def stub_template(hash)
90
+ view.view_paths.unshift(ActionView::FixtureResolver.new(hash))
91
+ end
92
+
88
93
  # Provides access to the params hash that will be available within the
89
94
  # view:
90
95
  #
@@ -137,13 +142,17 @@ module RSpec::Rails
137
142
 
138
143
  before do
139
144
  _include_controller_helpers
140
- controller.controller_path = _controller_path
145
+ if view.lookup_context.respond_to?(:prefixes)
146
+ # rails 3.1
147
+ view.lookup_context.prefixes << _controller_path
148
+ else
149
+ # rails 3.0
150
+ controller.controller_path = _controller_path
151
+ end
141
152
  controller.request.path_parameters["controller"] = _controller_path
142
153
  controller.request.path_parameters["action"] = _inferred_action unless _inferred_action =~ /^_/
143
154
  end
144
155
  end
145
-
146
- RSpec.configure &include_self_when_dir_matches('spec','views')
147
156
  end
148
157
  end
149
158
 
@@ -5,4 +5,34 @@ require 'rspec/rails/example/helper_example_group'
5
5
  require 'rspec/rails/example/view_example_group'
6
6
  require 'rspec/rails/example/mailer_example_group'
7
7
  require 'rspec/rails/example/routing_example_group'
8
- require 'rspec/rails/example/model_example_group'
8
+ require 'rspec/rails/example/model_example_group'
9
+
10
+ RSpec::configure do |c|
11
+ def c.escaped_path(*parts)
12
+ Regexp.compile(parts.join('[\\\/]'))
13
+ end
14
+
15
+ c.include RSpec::Rails::ControllerExampleGroup, :type => :controller, :example_group => {
16
+ :file_path => c.escaped_path(%w[spec controllers])
17
+ }
18
+ c.include RSpec::Rails::HelperExampleGroup, :type => :helper, :example_group => {
19
+ :file_path => c.escaped_path(%w[spec helpers])
20
+ }
21
+ if defined?(RSpec::Rails::MailerExampleGroup)
22
+ c.include RSpec::Rails::MailerExampleGroup, :type => :mailer, :example_group => {
23
+ :file_path => c.escaped_path(%w[spec mailers])
24
+ }
25
+ end
26
+ c.include RSpec::Rails::ModelExampleGroup, :type => :model, :example_group => {
27
+ :file_path => c.escaped_path(%w[spec models])
28
+ }
29
+ c.include RSpec::Rails::RequestExampleGroup, :type => :request, :example_group => {
30
+ :file_path => c.escaped_path(%w[spec (requests|integration)])
31
+ }
32
+ c.include RSpec::Rails::RoutingExampleGroup, :type => :routing, :example_group => {
33
+ :file_path => c.escaped_path(%w[spec routing])
34
+ }
35
+ c.include RSpec::Rails::ViewExampleGroup, :type => :view, :example_group => {
36
+ :file_path => c.escaped_path(%w[spec views])
37
+ }
38
+ end
@@ -1,44 +1,40 @@
1
1
  module RSpec
2
2
  module Rails
3
- if using_active_record?
3
+ if defined?(ActiveRecord)
4
4
  module Extensions
5
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
6
+ # :call-seq:
7
+ # ModelClass.should have(:no).records
8
+ # ModelClass.should have(1).record
9
+ # ModelClass.should have(n).records
10
+ #
11
+ # Extension to enhance <tt>should have</tt> on AR Model classes
12
+ def records
13
+ find(:all)
33
14
  end
15
+ alias :record :records
34
16
  end
35
- end
36
17
 
37
- class ::ActiveRecord::Base #:nodoc:
38
- extend RSpec::Rails::Extensions::ActiveRecord::ClassMethods
39
- include RSpec::Rails::Extensions::ActiveRecord::InstanceMethods
18
+ class ::ActiveRecord::Base #:nodoc:
19
+ extend RSpec::Rails::Extensions::ActiveRecord
20
+ end
40
21
  end
41
22
  end
42
23
  end
43
24
  end
44
25
 
26
+ module ::ActiveModel::Validations
27
+ # :call-seq:
28
+ # model.should have(:no).errors_on(:attribute)
29
+ # model.should have(1).error_on(:attribute)
30
+ # model.should have(n).errors_on(:attribute)
31
+ #
32
+ # Extension to enhance <tt>should have</tt> on AR Model instances.
33
+ # Calls model.valid? in order to prepare the object's errors
34
+ # object.
35
+ def errors_on(attribute)
36
+ self.valid?
37
+ [self.errors[attribute]].flatten.compact
38
+ end
39
+ alias :error_on :errors_on
40
+ end
@@ -1,6 +1,6 @@
1
1
  module RSpec
2
2
  module Rails
3
- if using_active_record?
3
+ if defined?(ActiveRecord)
4
4
  module FixtureSupport
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define :be_new_record do
2
+ match do |actual|
3
+ !actual.persisted?
4
+ end
5
+ end
@@ -3,13 +3,17 @@ module RSpec::Rails::Matchers
3
3
  extend RSpec::Matchers::DSL
4
4
 
5
5
  matcher :redirect_to do |destination|
6
- match_unless_raises Test::Unit::AssertionFailedError do |_|
6
+ match_unless_raises ActiveSupport::TestCase::Assertion do |_|
7
7
  assert_redirected_to destination
8
8
  end
9
9
 
10
- failure_message_for_should do
10
+ failure_message_for_should do |_|
11
11
  rescued_exception.message
12
12
  end
13
+
14
+ failure_message_for_should_not do |_|
15
+ "expected not to redirect to #{destination.inspect}, but did"
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -3,7 +3,7 @@ module RSpec::Rails::Matchers
3
3
  extend RSpec::Matchers::DSL
4
4
 
5
5
  matcher :render_template do |options, message|
6
- match_unless_raises Test::Unit::AssertionFailedError do |_|
6
+ match_unless_raises ActiveSupport::TestCase::Assertion do |_|
7
7
  options = options.to_s if Symbol === options
8
8
  assert_template options, message
9
9
  end
@@ -11,6 +11,10 @@ module RSpec::Rails::Matchers
11
11
  failure_message_for_should do
12
12
  rescued_exception.message
13
13
  end
14
+
15
+ failure_message_for_should_not do |_|
16
+ "expected not to render #{options.inspect}, but did"
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -2,10 +2,21 @@ module RSpec::Rails::Matchers
2
2
  module RoutingMatchers
3
3
  extend RSpec::Matchers::DSL
4
4
 
5
- matcher :route_to do |route_options|
6
- match_unless_raises Test::Unit::AssertionFailedError do |path|
5
+ matcher :route_to do |*route_options|
6
+ match_unless_raises ActiveSupport::TestCase::Assertion do |path|
7
7
  assertion_path = { :method => path.keys.first, :path => path.values.first }
8
- assert_routing(assertion_path, route_options)
8
+
9
+ path, options = *route_options
10
+
11
+ if path.is_a?(String)
12
+ controller, action = path.split("#")
13
+ options ||= {}
14
+ options.merge!(:controller => controller, :action => action)
15
+ else
16
+ options = path
17
+ end
18
+
19
+ assert_recognizes(options, assertion_path)
9
20
  end
10
21
 
11
22
  failure_message_for_should do
@@ -24,5 +35,15 @@ module RSpec::Rails::Matchers
24
35
  "expected #{path.inspect} not to be routable, but it routes to #{@routing_options.inspect}"
25
36
  end
26
37
  end
38
+
39
+ module RouteHelpers
40
+
41
+ %w(get post put delete options head).each do |method|
42
+ define_method method do |path|
43
+ { method.to_sym => path }
44
+ end
45
+ end
46
+
47
+ end
27
48
  end
28
49
  end
@@ -1,5 +1,4 @@
1
1
  require 'rspec/core/deprecation'
2
- require 'rspec/core/backward_compatibility'
3
2
  require 'rspec/matchers'
4
3
 
5
4
  module RSpec::Rails
@@ -21,5 +20,6 @@ end
21
20
  require 'rspec/rails/matchers/render_template'
22
21
  require 'rspec/rails/matchers/redirect_to'
23
22
  require 'rspec/rails/matchers/routing_matchers'
23
+ require 'rspec/rails/matchers/be_new_record'
24
24
  require 'rspec/rails/matchers/be_a_new'
25
25
  require 'rspec/rails/matchers/have_extension'
@@ -1,3 +1,6 @@
1
+ require 'active_support/core_ext'
2
+ require 'active_model'
3
+
1
4
  module RSpec
2
5
  module Rails
3
6
 
@@ -5,39 +8,55 @@ module RSpec
5
8
 
6
9
  module Mocks
7
10
 
8
- module InstanceMethods
9
- def valid?
10
- true
11
- end
12
-
11
+ module ActiveModelInstanceMethods
13
12
  def as_new_record
13
+ self.stub(:persisted?) { false }
14
14
  self.stub(:id) { nil }
15
15
  self
16
16
  end
17
17
 
18
- def new_record?
19
- !persisted?
18
+ def persisted?
19
+ true
20
20
  end
21
21
 
22
- def persisted?
23
- !!id
22
+ def respond_to?(message, include_private=false)
23
+ message.to_s =~ /_before_type_cast$/ ? false : super
24
24
  end
25
+ end
25
26
 
27
+ module ActiveRecordInstanceMethods
26
28
  def destroy
29
+ self.stub(:persisted?) { false }
27
30
  self.stub(:id) { nil }
28
31
  end
32
+
33
+ def [](key)
34
+ send(key)
35
+ end
36
+
37
+ def new_record?
38
+ !persisted?
39
+ end
29
40
  end
30
41
 
31
- # Creates a mock object instance for a +string_or_model_class+ with
32
- # common methods stubbed out. Additional methods may be easily stubbed
33
- # (via add_stubs) if +stubs+ is passed.
42
+ # Creates a test double representing +string_or_model_class+ with common
43
+ # ActiveModel methods stubbed out. Additional methods may be easily
44
+ # stubbed (via add_stubs) if +stubs+ is passed. This is most useful for
45
+ # impersonating models that don't exist yet.
34
46
  #
35
- # +model_class+ can be any of:
47
+ # NOTE that only ActiveModel's methods, plus <tt>new_record?</tt>, are
48
+ # stubbed out implicitly. <tt>new_record?</tt> returns the inverse of
49
+ # <tt>persisted?</tt>, and is present only for compatibility with
50
+ # extension frameworks that have yet to update themselves to the
51
+ # ActiveModel API (which declares <tt>persisted?</tt>, not
52
+ # <tt>new_record?</tt>).
53
+ #
54
+ # +string_or_model_class+ can be any of:
36
55
  #
37
56
  # * A String representing a Class that does not exist
38
57
  # * A String representing a Class that extends ActiveModel::Naming
39
58
  # * A Class that extends ActiveModel::Naming
40
- def mock_model(string_or_model_class, options_and_stubs = {})
59
+ def mock_model(string_or_model_class, stubs = {})
41
60
  if String === string_or_model_class
42
61
  if Object.const_defined?(string_or_model_class)
43
62
  model_class = Object.const_get(string_or_model_class)
@@ -61,57 +80,72 @@ It received #{model_class.inspect}
61
80
  EOM
62
81
  end
63
82
 
64
- id = options_and_stubs.has_key?(:id) ? options_and_stubs[:id] : next_id
65
- options_and_stubs = options_and_stubs.reverse_merge({
66
- :id => id,
67
- :destroyed? => false,
68
- :marked_for_destruction? => false
69
- })
70
- derived_name = "#{model_class.name}_#{id}"
71
- m = mock(derived_name, options_and_stubs)
72
- m.extend InstanceMethods
73
- m.extend ActiveModel::Conversion
74
- errors = ActiveModel::Errors.new(m)
75
- [:save, :update_attributes].each do |key|
76
- if options_and_stubs[key] == false
77
- errors.stub(:empty?) { false }
83
+ stubs = stubs.reverse_merge(:id => next_id)
84
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
85
+ stubs = stubs.reverse_merge(:destroyed? => false)
86
+ stubs = stubs.reverse_merge(:marked_for_destruction? => false)
87
+ stubs = stubs.reverse_merge(:blank? => false)
88
+
89
+ mock("#{model_class.name}_#{stubs[:id]}", stubs).tap do |m|
90
+ m.extend ActiveModelInstanceMethods
91
+ m.singleton_class.__send__ :include, ActiveModel::Conversion
92
+ m.singleton_class.__send__ :include, ActiveModel::Validations
93
+ if defined?(ActiveRecord)
94
+ m.extend ActiveRecordInstanceMethods
95
+ [:save, :update_attributes].each do |key|
96
+ if stubs[key] == false
97
+ m.errors.stub(:empty?) { false }
98
+ end
99
+ end
78
100
  end
101
+ m.__send__(:__mock_proxy).instance_eval(<<-CODE, __FILE__, __LINE__)
102
+ def @object.is_a?(other)
103
+ #{model_class}.ancestors.include?(other)
104
+ end
105
+ def @object.kind_of?(other)
106
+ #{model_class}.ancestors.include?(other)
107
+ end
108
+ def @object.instance_of?(other)
109
+ other == #{model_class}
110
+ end
111
+ def @object.respond_to?(method_name, include_private=false)
112
+ #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super
113
+ end
114
+ def @object.class
115
+ #{model_class}
116
+ end
117
+ def @object.to_s
118
+ "#{model_class.name}_#{to_param}"
119
+ end
120
+ CODE
121
+ yield m if block_given?
79
122
  end
80
- m.stub(:errors) { errors }
81
- m.__send__(:__mock_proxy).instance_eval(<<-CODE, __FILE__, __LINE__)
82
- def @object.is_a?(other)
83
- #{model_class}.ancestors.include?(other)
84
- end
85
- def @object.kind_of?(other)
86
- #{model_class}.ancestors.include?(other)
87
- end
88
- def @object.instance_of?(other)
89
- other == #{model_class}
90
- end
91
- def @object.respond_to?(method_name)
92
- #{model_class}.respond_to?(:column_names) && #{model_class}.column_names.include?(method_name.to_s) || super
93
- end
94
- def @object.class
95
- #{model_class}
96
- end
97
- def @object.to_s
98
- "#{model_class.name}_#{id}"
99
- end
100
- CODE
101
- yield m if block_given?
102
- m
103
123
  end
104
124
 
105
- module ModelStubber
106
- def connection
107
- raise RSpec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")
125
+ module ActiveModelStubExtensions
126
+ def as_new_record
127
+ self.stub(:persisted?) { false }
128
+ self.stub(:id) { nil }
129
+ self
108
130
  end
109
- def new_record?
110
- __send__(self.class.primary_key).nil?
131
+
132
+ def persisted?
133
+ true
111
134
  end
135
+ end
136
+
137
+ module ActiveRecordStubExtensions
112
138
  def as_new_record
113
139
  self.__send__("#{self.class.primary_key}=", nil)
114
- self
140
+ super
141
+ end
142
+
143
+ def new_record?
144
+ !persisted?
145
+ end
146
+
147
+ def connection
148
+ raise RSpec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")
115
149
  end
116
150
  end
117
151
 
@@ -121,46 +155,51 @@ EOM
121
155
  # stub_model(Model, hash_of_stubs)
122
156
  # stub_model(Model, instance_variable_name, hash_of_stubs)
123
157
  #
124
- # Creates an instance of +Model+ that is prohibited from accessing the
125
- # database*. For each key in +hash_of_stubs+, if the model has a
126
- # matching attribute (determined by asking it) are simply assigned the
127
- # submitted values. If the model does not have a matching attribute, the
128
- # key/value pair is assigned as a stub return value using RSpec's
129
- # mocking/stubbing framework.
158
+ # Creates an instance of +Model+ with +to_param+ stubbed using a
159
+ # generated value that is unique to each object.. If +Model+ is an
160
+ # +ActiveRecord+ model, it is prohibited from accessing the database*.
130
161
  #
131
- # <tt>new_record?</tt> is overridden to return the result of id.nil?
132
- # This means that by default new_record? will return false. If you want
162
+ # For each key in +hash_of_stubs+, if the model has a matching attribute
163
+ # (determined by asking it) are simply assigned the submitted values. If
164
+ # the model does not have a matching attribute, the key/value pair is
165
+ # assigned as a stub return value using RSpec's mocking/stubbing
166
+ # framework.
167
+ #
168
+ # <tt>persisted?</tt> is overridden to return the result of !id.nil?
169
+ # This means that by default persisted? will return true. If you want
133
170
  # the object to behave as a new record, sending it +as_new_record+ will
134
171
  # set the id to nil. You can also explicitly set :id => nil, in which
135
- # case new_record? will return true, but using +as_new_record+ makes the
172
+ # case persisted? will return false, but using +as_new_record+ makes the
136
173
  # example a bit more descriptive.
137
174
  #
138
175
  # While you can use stub_model in any example (model, view, controller,
139
176
  # helper), it is especially useful in view examples, which are
140
177
  # inherently more state-based than interaction-based.
141
178
  #
142
- # == Database Independence
143
- #
144
- # +stub_model+ does not make your examples entirely
145
- # database-independent. It does not stop the model class itself from
146
- # loading up its columns from the database. It just prevents data access
147
- # from the object itself. To completely decouple from the database, take
148
- # a look at libraries like unit_record or NullDB.
149
- #
150
179
  # == Examples
151
180
  #
152
181
  # stub_model(Person)
153
182
  # stub_model(Person).as_new_record
154
- # stub_model(Person, :id => 37)
183
+ # stub_model(Person, :to_param => 37)
155
184
  # stub_model(Person) do |person|
156
185
  # person.first_name = "David"
157
186
  # end
158
187
  def stub_model(model_class, stubs={})
159
- primary_key = model_class.primary_key.to_sym
160
- stubs = {primary_key => next_id}.merge(stubs)
161
188
  model_class.new.tap do |m|
162
- m.__send__("#{primary_key}=", stubs.delete(primary_key))
163
- m.extend ModelStubber
189
+ m.extend ActiveModelStubExtensions
190
+ if defined?(ActiveRecord) && model_class < ActiveRecord::Base
191
+ m.extend ActiveRecordStubExtensions
192
+ primary_key = model_class.primary_key.to_sym
193
+ stubs = stubs.reverse_merge(primary_key => next_id)
194
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[primary_key])
195
+ else
196
+ stubs = stubs.reverse_merge(:id => next_id)
197
+ stubs = stubs.reverse_merge(:persisted? => !!stubs[:id])
198
+ end
199
+ stubs = stubs.reverse_merge(:blank? => false)
200
+ stubs.each do |k,v|
201
+ m.__send__("#{k}=", stubs.delete(k)) if m.respond_to?("#{k}=")
202
+ end
164
203
  m.stub(stubs)
165
204
  yield m if block_given?
166
205
  end
@@ -1,7 +1,24 @@
1
1
  module RSpec::Rails
2
2
  module ModuleInclusion
3
+ # Deprecated as of rspec-rails-2.4
4
+ # Will be removed from rspec-rails-3.0
5
+ #
6
+ # This was never intended to be a public API and is no longer needed
7
+ # internally. As it happens, there are a few blog posts citing its use, so
8
+ # I'm leaving it here, but deprecated.
3
9
  def include_self_when_dir_matches(*path_parts)
10
+ instead = <<-INSTEAD
11
+
12
+
13
+ RSpec.configure do |c|
14
+ c.include self, :example_group => {
15
+ :file_path => /#{path_parts.join('\/')}/
16
+ }
17
+ end
18
+
19
+ INSTEAD
4
20
  lambda do |c|
21
+ RSpec.deprecate('include_self_when_dir_matches', instead, 'rails-3.0')
5
22
  c.include self, :example_group => {
6
23
  :file_path => Regexp.compile(path_parts.join('[\\\/]'))
7
24
  }
@@ -41,7 +41,7 @@ namespace :spec do
41
41
  ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers')
42
42
  ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers')
43
43
  ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib')
44
- ::CodeStatistics::TEST_TYPES << "Mailer specs" if File.exist?('spec/mailer')
44
+ ::CodeStatistics::TEST_TYPES << "Mailer specs" if File.exist?('spec/mailers')
45
45
  ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing')
46
46
  ::CodeStatistics::TEST_TYPES << "Request specs" if File.exist?('spec/requests')
47
47
  end