zargony.com

#![desc = "Random thoughts of a software engineer"]

Flash not accessible in tests (TypeError: can't convert String into Integer)

This morning I faced a strange problem when trying to write RSpec tests for a user login action. Trying to setting the flash before doing a request, resulted in an unexpected Exception:

TypeError in 'SessionController logging in with correct credentials should redirect to the url_after_login url if given'
can't convert String into Integer
.../vendor/plugins/rspec_on_rails/lib/spec/rails/dsl/behaviour/functional.rb:80:in 'flash'
./spec/controllers/session_controller_spec.rb:52:
./script/spec:4:

After some debugging, I found out that session delegates to @request.session and flash delegates to @response.session['flash']. It turned out, that before doing a test-request (by calling get or post), @response.session is initialized to an empty array (in ActionController::AbstractResponse#initilaize), while @request.session is properly initialized to ActionController::TestSession.

So, trying to access the flash delegates to @response.session['flash'], which throws a TypeError, because @response.session is an array and an array index is expected to be an integer, not a string.

This problem seems to only occur when trying to access the flash in a test before doing the actual request (which I needed to do in my case to set the URL that the controller should redirect to after a login). Setting session values itself doesn't seem to be affected, because session delegates to @request.session, which is properly initialized with ActionController::TestSession.new.

If you also face this problem in your tests or specs, you can easily work around it by manually initializing the response's session. Just add

response.session = ActionController::TestSession.new

to your setup method (or to your before(:each) block if you use RSpec).

Somebody already submitted a ticket concerning this (or a similar) problem 3 months ago: Ticket #8939. Unfortunately, it was flagged incomplete. Lets see if I can put together and submit a patch later today...

Update

The above workaround does indeed fix the TypeError problem, but setting the flash before doing the test-request doesn't seem to affect the flash that the tested controller sees. Therefore, values set to the flash do not affect the controller. It looks like this problem goes deeper into the rails core than I was expecting. For now, here's a (working) workaround to set the flash for now. Instead of flash[:key]=value, use:

controller.send(:flash)[:key] = value

Update 2007-09-24

You live and learn... As mpalmer points out in Ticket #8939, you aren't supposed to access the flash hash before doing a test request. Instead, the flash can be set by giving a 4th parameter to the get or post method:

get :login, { :username => ..., :password => ... }, {}, { :url_after_login => ... }

This isn't as intuitive as I expected it to be (like setting the flash hash directly), but it works fine...