Check all your Rails Routes at once
I think sometimes it’s a good idea to make broad, sweeping checks across your whole application. Sometimes, you want to say categorically: “This is the way we do things. Time to draw an RSpec shaped line in the sand.”
At work, I recently stumbled across a test case that was hidden deep inside an integration test, which looked like this:
# hundreds of lines above
it "does not include umlauts" do
expect(confirmation_path =~ URI::UNSAFE).to be_nil
end
# hundreds of lines below
This had a good historic reason: The path in question is /bestaetigen
(the german word for “confirm”)
and, very much on purpose, does not include an ä
(even though that would be theoretically possible).
Someone almost changed it to /bestätigen
once, but stopped at the last second because nobody was sure if this would work with our external CRM, HubSpot. Maybe the CRM doesn’t support umlauts? Maybe some other part of our system doesn’t? Who knows?
In any case, the developer decided to revert the change and to cement this decision with a test: This special URL should never contain an umlaut.
This is not a bad idea, but personally, I feel it’s a bit short sighted. If we’re afraid of this change, then we should probably also be afraid for all our paths!
I decided to delete the test case and add a new file:
# spec/routing/route_path_naming_spec.rb
require "rails_helper"
describe "list of all paths" do
it "does not contain characters that need to be escaped" do
# Get an array of all paths in the whole Rails app.
# No, `.routes.routes` is not a typo. 🙃
paths = Rails.application.routes.routes.map do |route|
route.path.spec.to_s
end
paths.each do |path|
expect(path).not_to match(URI::UNSAFE)
end
end
end
This spec runs through all our static routes in milliseconds. It even found two offenders that had slipped through, which was fun.
I’m really happy with how this turned out. I think there is value in specs that assert something about the system as a whole.
“We don’t use umlauts in paths“ could have been a rule in the README.md
, or a sentence in a long forgotten Confluence document. But now, it’s part of the app and will always be enforced. This is a kind of automation I really enjoy.