Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functional tests for flash messages after redirect

I'm not sure how to check flash messages with functional tests. My problem has something to do with redirect_to because my test for flash[:error] DOES pass. Here is what my create action looks like:

def create
  @user = User.new(params[:user])
  if @user.save
    redirect_to @user, flash: { success: "Welcome!" }
  else
    flash.now[:error] = "Signup failed:"
    render 'new'
  end
end

I have a passing test for flash[:error] that 1) checks that flash[:error] is the correct value, 2) checks the page for the flash div, and 3) gets a new page and checks that the flash message has been cleared. I tried to make an analogous test for flash[:success] but it is failing when it tries to find the div on the page. Here is the failing test:

  should "show flash[:success] message" do
    post :create, user: { name: "valid user",
                          password: "userpw",
                          password_confirmation: "userpw",
                          email: "[email protected]" }        
    assert_redirected_to user_path(assigns(:user)), "user isn't being redirected to their page after registration"
    assert_equal flash[:success], "Welcome!", "flash[:success] isn't being set properly"
    assert_select "div.alert-success", flash[:success]
    get :new
    assert flash.empty?, "there shouldn't be any flash messages left after getting new page"
  end

When I test this manually in a browser, it behaves as expected, but I'm not sure how to write the tests to reflect this. The first two assertions are passing (so the test recognizes that it is being redirected correctly, and that flash[:success] is being set to "Welcome!"). Based on that, I would think the assert_select statement would be able to find the div, but if fails with this error message:

Expected at least 1 element matching "div.alert-success", found 0.

So I tried adding puts @response.body right before the assert_select statement, which printed this:

<html><body>You are being <a href="http://test.host/users/3">redirected</a>.</body></html>

This explains why it couldn't find the flash div, but I'm not sure how to "finish" the redirect so that it renders the correct view with the flash div. If I add a get statement right before the assert_select statement, the test passes, because now @response.body has the flash div. However, this doesn't seem like an effective test because it doesn't reflect the behavior of my application - the user doesn't have to make a new request to see the flash message.

It also doesn't guarantee that the flash message shows up on the correct page. If I use the statement get :new, the assertion still passes, which implies that the flash message would be present on the users#new view. Even if I use

get :show, id: assigns(:user).id  

to make sure the flash message shows up on the correct page, this still feels wrong because 1) now it is a success response instead of a redirect response, and 2) it still doesn't explain why I have to manually request ANY page to find the flash div - according to the docs, "The flash is a special part of the session which is cleared with each request."

Anyway, is there a way to test that a flash message shows up correctly after a redirect?

like image 986
bentrevor Avatar asked Dec 15 '12 19:12

bentrevor


1 Answers

I'm answering this question from the POV that what you're trying to test would be better tested elsewhere. If you're having a lot of trouble setting up the test, odds are there's a better way to test it :)

  1. Testing that flash messages get cleared - flash messages are built into Rails, so it doesn't make a lot of sense to test something that the framework is handling for you.

  2. Testing that elements show up on the page - view specs don't belong in controllers. Here is a description of the role of the controller:

    A controller can thus be thought of as a middle man between models and views. It makes the model data available to the view so it can display that data to the user.

    The controller is supposed to make data available to the view, not to specify how the data gets rendered. Imagine if you changed the name of your CSS class, then you'd have to also update your controller specs.

    If you really want to test that flash messages show up, I would use an integration test.

like image 102
Fiona T Avatar answered Oct 22 '22 01:10

Fiona T