Keep the Flash and Test it, too.

  • Posted by Mike Naberezny in Ruby,Testing

    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 a flash attribute and the session doesn’t have a FlashHash. 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 the xhr 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.