Created
October 31, 2024 11:26
-
-
Save fractaledmind/410e519ccd51445cc10c3408b5f24d77 to your computer and use it in GitHub Desktop.
Test that ensures you have no controllers with public methods that do not have a corresponding route defined.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RoutingTest < ActionDispatch::IntegrationTest | |
IGNORED_CONTROLLERS = Set[ | |
"Rails::MailersController" | |
] | |
test "no unrouted actions (public controller methods)" do | |
actions_by_controller.each do |controller_path, actions| | |
controller_name = "#{controller_path.camelize}Controller" | |
next if IGNORED_CONTROLLERS.include?(controller_name) | |
controller = Object.const_get(controller_name) | |
public_methods = controller.public_instance_methods(_include_super = false).map(&:to_s) | |
unrouted_actions = public_methods - actions | |
assert_empty( | |
unrouted_actions, | |
"#{controller_name} has unrouted actions (public methods). These should probably be private" | |
) | |
end | |
end | |
private | |
def actions_by_controller | |
{}.tap do |controllers| | |
@routes.routes.each do |route| | |
controller = route.requirements[:controller] | |
action = route.requirements[:action] | |
next unless controller && action | |
(controllers[controller] ||= []) << action | |
end | |
end | |
end | |
end |
One nice addition is to list the actual methods in the message:
edit: not necessary with minitest, see https://bsky.app/profile/fractaledmind.bsky.social/post/3l7susvs6rk2v
assert_empty(
unrouted_actions,
- "#{controller_name} has unrouted actions (public methods). These should probably be private"
+ "#{controller_name} has unrouted actions (public methods): #{unrouted_actions.map(&:to_sym)}. These should probably be private"
)
And here's an RSpec version if anyone is looking for one (with my improvement from above and :aggregate_failures
to find all cases immediately).
RSpec.describe "Routing" do
IGNORED_CONTROLLERS = Set[
"Rails::MailersController"
]
it "has no unrouted actions (public controller methods)", :aggregate_failures do
actions_by_controller.each do |controller_path, actions|
controller_name = "#{controller_path.camelize}Controller"
next if IGNORED_CONTROLLERS.include?(controller_name)
controller = Object.const_get(controller_name)
public_methods = controller.public_instance_methods(_include_super = false).map(&:to_s)
unrouted_actions = public_methods - actions
expect(unrouted_actions).to be_empty,
"#{controller_name} has unrouted actions (public methods): #{unrouted_actions.map(&:to_sym)}. These should probably be private"
end
end
private
def actions_by_controller
{}.tap do |controllers|
Rails.application.routes.routes.each do |route|
controller = route.requirements[:controller]
action = route.requirements[:action]
next unless controller && action
(controllers[controller] ||= []) << action
end
end
end
end
There is a gem that does this. It all so does the inverse, checks for defined routes that don’t have corresponding methods.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
As soon as the assertion for one controller fails the test ends! I made a small tweak to get a result for all controllers in one go. Thank you, I have a little cleanup to do 😳 🙏