---
title: Rails Path Helpers and the Mystery of the Missing Route Key
teaser: Rails Path Helpers embark on a quest to find a missing route key only for
  the tester to discover that errors lie just around the bend...
tags: rails,ruby
author: Stephanie Viccari
published_on: 2018-11-09
---

While working on a Rails project and adding test coverage for an existing view,
I ran into the following error:

```Ruby
ActionView::Template::Error:
  No route matches { :action => "edit", :controller => "users" }, missing required keys: [:id]
```

This error is informing the reader that the path to edit a user (ex:
`users/:id/edit` can't be built because the required `:id` value is missing.

The intriguing part of this error is that locally this page renders the correct
path to edit a user. Yet, attempting to render the same view in isolation, in a
view spec, is raising an error.

To understand what's happening, let's take a look at the existing code:

```Ruby
# users_controller.rb

class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end
```

```Ruby
# routes.rb

resources :users
```

```Ruby
# users/show.html

<p>First Name: <%= @user.first_name %></p>
<p><%= link_to "Edit User", edit_user_path %></p>
```

Now, let's take a look at the view spec that renders the user show page:

```Ruby
# spec/views/users/show.html.erb_spec.rb

describe "users/show", type: :view do
  it "displays information about the user" do
    user = create(:user)
    assign(:user, user)

    render

    expect(page).to have_text(user.first_name)
  end
end
```

With the current code, the expectation is never reached because `render` is
raising an `ActionView::Template` error.

So why does the `edit_user_path` fail to build in the test but successfully
builds when a user visits the same page?

## Searching For Missing Route Values

Under the hood, Rails path helpers use ActionDispatch to generate paths that map
to routes defined in `routes.rb`. When the path helper is not provided the
necessary route key, two outcomes may occur:

1) The missing route key is filled in based on the current request.
2) An error is raised, stating the route cannot be built.

When the route key is missing, ActionDispatch will examine the current request
for an identical key that matches the missing key. In this specific example,
when a user navigates to `GET /users/123`, the `id: 123` value is available in
the current request. ActionDispatch will use the `:id` value as a
substitute for the missing route key and build the path `/users/123/edit`.

ActionDispatch's ability to [search the current request options] and fill in the
missing value allows path helpers to build a valid path, even when the required
information is not provided.

In our test scenario, a request has not been issued to `GET /users/123` and
ActionDispatch is unable to infer the missing `:id` value. This results in the
test raising an `ActionView::Template` error.

[search the current request options]: https://github.com/rails/rails/blob/d7f48c9c39befaf23ccd63e0248a3bd5bf295ee5/actionpack/lib/action_dispatch/routing/route_set.rb#L799

## Don't Make ActionDispatch Guess

To resolve the error and restore the view's ability to render without
relying on values in the current request, supply the path helper with the
necessary route key value:

```Ruby
# users/show.html

<p>First Name: <%= @user.first_name %></p>
<p><%= link_to "Edit User", edit_user_path(@user.id) %></p>
```

If you prefer, you can avoid the `.id` call and pass the object instance as
Rails will derive the value by calling `to_param` on the object:

```Ruby
# users/show.html

<p>First Name: <%= @user.first_name %></p>
<p><%= link_to "Edit User", edit_user_path(@user) %></p>
```

## Summary

Avoid relying on path helpers to play the role of detective by searching for the
missing value. There's a chance ActionDispatch will infer the wrong value and
this approach prevents the view template from being rendered in isolation. A
better approach is to provide the path helper with the necessary value(s) to
build a valid path.
