Prefer LiveView's assert_redirect over assert_redirected
Someone in my Testing LiveView course recently asked,
When would you use
assert_redirected
instead ofassert_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
.