Keep the Flash and Test it, too.
-
The
flash
in Ruby on Rails is a special hash that is stored in the session. Its contents are available to the next request and cleared out immediately after.This makes is very useful one-time events, such as an item being added to a shopping cart:
flash[:success] = "#{cart.items.size} items added to your cart."
The
flash[:success]
will be available for the next request only.However, Ajax requests will also clear the flash. This can lead to the sometimes mysterious problem of the flash contents disappearing before their intended request because an intermediate Ajax request caused them to be cleared.
The solution is to explicitly preserve the flash contents in actions that could unintentionally clear it. This is done using
flash.keep
:flash.keep
The current contents of the flash will then be preserved for the next request. You can also pass a specific key such as
flash.keep(:foo)
.On one application I work on, we make Ajax requests periodically on a timer. Putting
flash.keep
in these Ajax actions was a simple way to make sure they didn’t unexpectedly gobble the flash contents.Of course, once adding
flash.keep
, we also want to add a functional test for it. You might try doing this:def test_something_keeps_flash @request.flash[:foo] = 'bar' xhr :get, :something assert_response :success assert_equal 'bar', @response.flash[:foo] end
You’d be close but wrong. That would seem like a natural way to do it but unfortunately, the flash can’t be populated that way due to how
TestRequest
was built. It doesn’t have aflash
attribute and the session doesn’t have aFlashHash
. However, that won’t stop us from writing our test.We could try monkeypatching but I try to avoid clever things like that. This problem is easily remedied with a helper in
test_helper.rb
:def make_flash_hash(with_contents = {}) returning ActionController::Flash::FlashHash.new do |flash_hash| flash_hash.update with_contents end end
Our test case now can now use the simple
make_flash_hash
helper to populate the request’s session with flash:def test_something_keeps_flash @request.session['flash'] = make_flash_hash(:foo => 'bar') xhr :get, :something assert_response :success assert_equal 'bar', @response.flash[:foo] end
In addition to using
@request.session
as shown above, you could also populate the session in thexhr
method directly. I like the former for readability.Whichever way you choose to do it, writing the functional test will ensure that you’ll continue to preserve the flash when the application changes later.