Skip to content

Commit d46d5ce

Browse files
committed
Make with_routing test helper work for integration tests
Adds support for with_routing test helpers in ActionDispatch::IntegrationTest. Previously, this helper didn't work in an integration context because the rack app and integration session under test were not mutated. Because controller tests are integration tests by default, we should support test routes for these kinds of test cases as well.
1 parent 04f29cb commit d46d5ce

File tree

13 files changed

+258
-110
lines changed

13 files changed

+258
-110
lines changed

actionpack/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Add support for `with_routing` test helper in `ActionDispatch::IntegrationTest`
2+
3+
*Gannon McGibbon*
4+
15
* Remove deprecated support to set `Rails.application.config.action_dispatch.show_exceptions` to `true` and `false`.
26

37
*Rafael Mendonça França*

actionpack/lib/action_dispatch/testing/assertions/routing.rb

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,65 @@ module Assertions
1111
module RoutingAssertions
1212
extend ActiveSupport::Concern
1313

14+
module WithIntegrationRouting # :nodoc:
15+
extend ActiveSupport::Concern
16+
17+
module ClassMethods
18+
def with_routing(&block)
19+
old_routes = nil
20+
old_integration_session = nil
21+
22+
setup do
23+
old_routes = app.routes
24+
old_integration_session = integration_session
25+
create_routes(&block)
26+
end
27+
28+
teardown do
29+
reset_routes(old_routes, old_integration_session)
30+
end
31+
end
32+
end
33+
34+
def with_routing(&block)
35+
old_routes = app.routes
36+
old_integration_session = integration_session
37+
create_routes(&block)
38+
ensure
39+
reset_routes(old_routes, old_integration_session)
40+
end
41+
42+
private
43+
def create_routes
44+
app = self.app
45+
routes = ActionDispatch::Routing::RouteSet.new
46+
rack_app = app.config.middleware.build(routes)
47+
https = integration_session.https?
48+
host = integration_session.host
49+
50+
app.instance_variable_set(:@routes, routes)
51+
app.instance_variable_set(:@app, rack_app)
52+
@integration_session = Class.new(ActionDispatch::Integration::Session) do
53+
include app.routes.url_helpers
54+
include app.routes.mounted_helpers
55+
end.new(app)
56+
@integration_session.https! https
57+
@integration_session.host! host
58+
@routes = routes
59+
60+
yield routes
61+
end
62+
63+
def reset_routes(old_routes, old_integration_session)
64+
old_rack_app = app.config.middleware.build(old_routes)
65+
66+
app.instance_variable_set(:@routes, old_routes)
67+
app.instance_variable_set(:@app, old_rack_app)
68+
@integration_session = old_integration_session
69+
@routes = old_routes
70+
end
71+
end
72+
1473
module ClassMethods
1574
# A helper to make it easier to test different route configurations.
1675
# This method temporarily replaces @routes with a new RouteSet instance
@@ -44,6 +103,26 @@ def setup # :nodoc:
44103
super
45104
end
46105

106+
# A helper to make it easier to test different route configurations.
107+
# This method temporarily replaces @routes with a new RouteSet instance.
108+
#
109+
# The new instance is yielded to the passed block. Typically the block
110+
# will create some routes using <tt>set.draw { match ... }</tt>:
111+
#
112+
# with_routing do |set|
113+
# set.draw do
114+
# resources :users
115+
# end
116+
# assert_equal "/users", users_path
117+
# end
118+
#
119+
def with_routing(&block)
120+
old_routes, old_controller = @routes, @controller
121+
create_routes(&block)
122+
ensure
123+
reset_routes(old_routes, old_controller)
124+
end
125+
47126
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
48127
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
49128
#
@@ -167,26 +246,6 @@ def assert_routing(path, options, defaults = {}, extras = {}, message = nil)
167246
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
168247
end
169248

170-
# A helper to make it easier to test different route configurations.
171-
# This method temporarily replaces @routes with a new RouteSet instance.
172-
#
173-
# The new instance is yielded to the passed block. Typically the block
174-
# will create some routes using <tt>set.draw { match ... }</tt>:
175-
#
176-
# with_routing do |set|
177-
# set.draw do
178-
# resources :users
179-
# end
180-
# assert_equal "/users", users_path
181-
# end
182-
#
183-
def with_routing(&block)
184-
old_routes, old_controller = @routes, @controller
185-
create_routes(&block)
186-
ensure
187-
reset_routes(old_routes, old_controller)
188-
end
189-
190249
# ROUTES TODO: These assertions should really work in an integration context
191250
def method_missing(selector, *args, &block)
192251
if defined?(@controller) && @controller && defined?(@routes) && @routes && @routes.named_routes.route_defined?(selector)

actionpack/lib/action_dispatch/testing/integration.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ module Behavior
657657
included do
658658
include ActionDispatch::Routing::UrlFor
659659
include UrlOptions # don't let UrlFor override the url_options method
660+
include ActionDispatch::Assertions::RoutingAssertions::WithIntegrationRouting
660661
ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
661662
@@app = nil
662663
end

actionpack/test/abstract_unit.rb

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,23 @@ class TestCase
9191
end
9292

9393
class RoutedRackApp
94+
class Config < Struct.new(:middleware)
95+
end
96+
9497
attr_reader :routes
9598

9699
def initialize(routes, &blk)
97100
@routes = routes
98-
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
101+
@stack = ActionDispatch::MiddlewareStack.new(&blk)
102+
@app = @stack.build(@routes)
99103
end
100104

101105
def call(env)
102-
@stack.call(env)
106+
@app.call(env)
107+
end
108+
109+
def config
110+
Config.new(@stack)
103111
end
104112
end
105113

@@ -150,19 +158,6 @@ def self.stub_controllers(config = ActionDispatch::Routing::RouteSet::DEFAULT_CO
150158
yield DeadEndRoutes.new(config)
151159
end
152160

153-
def with_routing(&block)
154-
temporary_routes = ActionDispatch::Routing::RouteSet.new
155-
old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
156-
old_routes = SharedTestRoutes
157-
silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
158-
159-
yield temporary_routes
160-
ensure
161-
self.class.app = old_app
162-
remove!
163-
silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
164-
end
165-
166161
def with_autoload_path(path)
167162
path = File.join(__dir__, "fixtures", path)
168163
Zeitwerk.with_loader do |loader|

actionpack/test/controller/flash_test.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,14 @@ def get(path, **options)
377377
super(path, **options)
378378
end
379379

380+
def app
381+
@app ||= self.class.build_app do |middleware|
382+
middleware.use ActionDispatch::Session::CookieStore, key: SessionKey
383+
middleware.use ActionDispatch::Flash
384+
middleware.delete ActionDispatch::ShowExceptions
385+
end
386+
end
387+
380388
def with_test_route_set
381389
with_routing do |set|
382390
set.draw do
@@ -385,12 +393,6 @@ def with_test_route_set
385393
end
386394
end
387395

388-
@app = self.class.build_app(set) do |middleware|
389-
middleware.use ActionDispatch::Session::CookieStore, key: SessionKey
390-
middleware.use ActionDispatch::Flash
391-
middleware.delete ActionDispatch::ShowExceptions
392-
end
393-
394396
yield
395397
end
396398
end

actionpack/test/controller/integration_test.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,17 @@ class RackLintIntegrationTest < ActionDispatch::IntegrationTest
172172
get "/", to: ->(_) { [200, {}, [""]] }
173173
end
174174

175-
@app = self.class.build_app(set) do |middleware|
176-
middleware.unshift Rack::Lint
177-
end
178-
179175
get "/"
180176

181177
assert_equal 200, status
182178
end
183179
end
180+
181+
def app
182+
@app ||= self.class.build_app do |middleware|
183+
middleware.unshift Rack::Lint
184+
end
185+
end
184186
end
185187

186188
# Tests that integration tests don't call Controller test methods for processing.

actionpack/test/dispatch/request/query_string_parsing_test.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,19 @@ def test_array_parses_without_nil
157157
end
158158

159159
private
160+
def app
161+
@app ||= self.class.build_app do |middleware|
162+
middleware.use(EarlyParse)
163+
end
164+
end
165+
160166
def assert_parses(expected, actual)
161167
with_routing do |set|
162168
set.draw do
163169
ActionDispatch.deprecator.silence do
164170
get ":action", to: ::QueryStringParsingTest::TestController
165171
end
166172
end
167-
@app = self.class.build_app(set) do |middleware|
168-
middleware.use(EarlyParse)
169-
end
170173

171174
get "/parse", params: actual
172175
assert_response :ok

actionpack/test/dispatch/request_id_test.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ def index
5555
end
5656
end
5757

58+
setup do
59+
@header = "X-Request-Id"
60+
end
61+
5862
test "request id is passed all the way to the response" do
5963
with_test_route_set do
6064
get "/"
@@ -70,25 +74,28 @@ def index
7074
end
7175

7276
test "using a custom request_id header key" do
73-
with_test_route_set(header: "X-Tracer-Id") do
77+
@header = "X-Tracer-Id"
78+
with_test_route_set do
7479
get "/"
7580
assert_match(/\w+/, @response.headers["X-Tracer-Id"])
7681
end
7782
end
7883

7984
private
85+
def app
86+
@app ||= self.class.build_app do |middleware|
87+
middleware.use Rack::Lint
88+
middleware.use ActionDispatch::RequestId, header: @header
89+
middleware.use Rack::Lint
90+
end
91+
end
92+
8093
def with_test_route_set(header: "X-Request-Id")
8194
with_routing do |set|
8295
set.draw do
8396
get "/", to: ::RequestIdResponseTest::TestController.action(:index)
8497
end
8598

86-
@app = self.class.build_app(set) do |middleware|
87-
middleware.use Rack::Lint
88-
middleware.use ActionDispatch::RequestId, header: header
89-
middleware.use Rack::Lint
90-
end
91-
9299
yield
93100
end
94101
end

0 commit comments

Comments
 (0)