Prefer LiveView's assert_redirect over assert_redirected

Someone in my Testing LiveView course recently asked,

When would you use assert_redirected instead of assert_redirect?

You see, LiveView has two test helpers to assert if we’ve been redirected to another page: assert_redirected/2 and assert_redirect/2.

And as you can see, the two helpers look almost identical:

assert_redirected(view, "/posts")
assert_redirect(view, "/posts")

Just by looking at that, you might think we want to use assert_redirected/2.

After all, when you write an assertion, you expect something to have happened in the past. Think of how you’d write a LiveView test in plain English:

  • Visit the post composer in /posts/new
  • Create a new post: “Going to visit Rivendell next week!”
  • Assert we’ve been redirected to the posts page

But, in reality, I always prefer using assert_redirect/3 instead of assert_redirected/2.

Why? 🤔

Versions of assert_redirect/3

If you look at the implementations of assert_redirected/2 and assert_redirect/2, you’ll see that they’re both specific versions of a third helper called assert_redirect/3.

assert_redirect/3 takes three arguments:

  • the view,
  • the path we expect to redirect to, and
  • an optional timeout in milliseconds.

That timeout determines how long to wait until the redirect happens before the test fails.

assert_redirected/2 passes 0 as the third argument:

def assert_redirected(view, to) do
  assert_redirect(view, to, 0)
end

assert_redirect/2 passes 100 (or really, the default ExUnit timeout) as the third argument:

def assert_redirect(view, to) do
  assert_redirect(view, to, 100)
end

So, the question is, why should we prefer a timeout of 100 to 0?

assert_redirect/3 is all about messages

In reality, the test process is not waiting on the actual page redirect. Instead, it’s waiting to receive a message in its inbox. If it doesn’t get the message after timeout milliseconds, we get an error.

That might seem odd if you’re only thinking about Elixir from a functional perspective. But if you remember everything in Elixir runs in a process, you’ll realize that the test process has a mailbox just like any other.

And in tests, it’s fairly common (and convenient) to send messages to the test process as confirmation that some side effect happened. Even ExUnit has a built-in assert_receive/3 helper to test that messages have been received.

So, in theory, we could use assert_redirected/2 if we’re absolutely sure the redirection has already happened in the past. But, in practice, we rarely need that kind of certainty.

Instead, we’re trying to make sure the redirect happens, and since we are aware we’re dealing with message-passing, we’re okay waiting for the message to arrive.

Of course, we don’t want to wait forever, so 100ms is a good default. And if we need more time, we can always pass a larger timeout to assert_redirect/3.

Want my latest thoughts, posts, and projects in your inbox?

    I will never send you spam. Unsubscribe any time.