zargony.com

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

Testing Cookies in Rails 2.3

While moving some applications to Rails 2.3 recently, I stumbled across some problems with testing cookies. E.g. this blog uses cookies to remember your name, email address and web url if you leave a comment. This way, you don't need to re-enter these information the next time you leave a comment. These cookies are set by the controller action that creates new comments (in CommentsController#create):

cookies['blog_visitor_name'] = { :value => @comment.name, :expires => 1.year.from_now }

This is the extended form of setting a cookie (a hash is used to not only set the cookies value but also the expiration time). There are several more hash options available which are described in the ActionController#cookies documentation. The basic form (which only sets a simple session cookie that is valid until the visitor closes the browser) uses a simple string:

cookies['cookie_name'] = 'cookie_value'

This form of setting and getting cookies in a controller has been there for quite some time and actually hasn't changed in Rails 2.3. But what has changed, is the availability of cookies in functional tests. Before Rails 2.3, you needed to create a CGI::Cookie object for each cookie of a request. After the request, you could then check the cookies accessor and assert correct attributes of a cookie:

def test_comment_creation_overwrites_visitor_cookies
  @request.cookies['blog_visitor_name'] = CGI::Cookie.new('blog_visitor_name', 'Fred F.')
  post :create, { ... }
  assert_equal 'Fred', cookies['blog_visitor_name'].value
  assert_equal '/', cookies['blog_visitor_name'].path
  assert cookies['blog_visitor_name'].expires > 364.days.from_now
end

As you can see, this test first sets a cookie by creating a CGI:Cookie object and requests the create action. This simulates a user who previously used "Fred F." as his name and now posts a new comment using the name "Fred". After processing the request, the test checks if the user's cookie is update to the new name he gave. Furthermore it tests that the newly created cookie attributes (path and expire time) are properly set.

With Rails 2.3, usage of cookies in tests were modified to match usage in the controller itself. The above test now looks like this:

def test_comment_creation_overwrites_visitor_cookies
  @request.cookies['blog_visitor_name'] = 'Fred F.'
  post :create, { ... }
  assert_equal 'Fred', cookies['blog_visitor_name']
  #assert_equal '/', cookies['blog_visitor_name']...???
  #assert cookies['blog_visitor_name']...??? > 364.days.from_now
end

Since the cookies accessor now returns the cookie value only (like in controllers), it is easier to test wether a cookie was correctly set to the expected value. However, extended information like the cookie path and the expiration time are not available anymore and therefore we cannot test anymore if these attributes were correctly set by the controller.

So how can you test cookie attributes with Rails 2.3? Unfortunately you can't. The cookies accessor in tests parses the content of the Set-Cookie header of a response and builds a hash of cookies which were set by the controller action. Unfortunately, it only gathers the cookie name and value and does not parse the rest of the data. The only way that I know of to test cookie attributes is to manually parse the contents of the Set-Cookie header for now.