Examining the Contents of CookieProblemContributed by: Evan Henshaw-Plath (rabble) Your application uses cookies. You want to test your application's ability to create and retrieve them with a functional test. SolutionMost of the time you'll store information in the session, but there are cases when you need to save limited amounts of information in the cookie itself. Create a controller that sets a cookie to store page color: app/controller/cookie_controller.rb: class CookieController < ApplicationController def change_color @page_color = params[:color] if is_valid_color? @page_color ||= cookies[:page_color] cookies[:page_color] = { :value => @page_color, :expires => Time.now + 1.year, :path => '/', :domain => 'localhost' } if @page_color end private def is_valid_color? valid_colors = ['blue', 'green', 'black', 'white'] valid_colors.include? params[:color] end end Now, create a test that verifies that the values of the cookie are set correctly by the controller: test/functional/cookie_controller_test.rb: def test_set_cookie post :change_color, {:color => 'blue'} assert_response :success assert_equal '/', cookies['page_color'].path assert_equal 'localhost', cookies['page_color'].domain assert_equal 'blue', cookies['page_color'].value.first assert 350.days.from_now < cookies['page_color'].expires end To fully test cookies, you need to test that your application is not only setting the cookies, but also correctly reading them when passed in with a request. To do that you need to create a CGI::Cookie object and add that to the simulated test Request object, which is set up before every test in the setup method. test/functional/cookie_controller_test.rb: def test_read_cookie request.cookies['page_color'] = CGI::Cookie.new( 'name' => 'page_color', 'value' => 'black', 'path' => '/', 'domain' => 'localhost') post :change_color assert_response :success assert_equal 'black', cookies['page_color'].value.first assert 350.days.from_now < cookies['page_color'].expires end DiscussionIf you are using cookies in your application, it is important for your tests to cover them. If you don't test the cookies you use, you will be missing a critical aspect of your application. Cookies that fail to be set or read correctly can prove difficult to track down and debug unless you write functional tests. In this recipe we've tested both the creation and reading of cookie objects. When you are creating cookies, it's important to test both that they are created correctly and that your controller does the right thing when it detects a cookie. If you only test either the creation or the reading of cookies, you introduce the possibility of undetected and untested bugs. Cookies in Rails are based on the CGI::Cookie class and are made available in the controller and functional tests via the cookie's object. Each individual cookie appears to be a hash, but it's a special cookie hash that responds to all the methods for cookie options. If you do not give a cookie object an expiration date, it will be set to expire with the browser's session. A cookie is inserted into the response object for the HTTP headers via the to_s (to string) method, which serializes the cookie. When you are debugging cookies, it is often useful to print the cookie in the breakpointer. When you print the cookie, you can examine exactly what is getting sent to the browser. irb(test_set_cookie(CookieControllerTest)):001:0> cookies['page_color'].to_s => "page_color=blue; domain=localhost; path=/; expires=Mon, 07 May 2007 04:38:18 GMT" When debugging cookie issues, it is often important to turn on your browser's cookie tracking. Tracking can show you what the cookie actually looks like to the browser. Once you have the actual cookie string, you can pass it back in to the cookie object so your tests are driven by real-world test data. test/functional/cookie_controller_test.rb: def test_cookie_from_string cookie_parsed = CGI::Cookie.parse( "page_color=green; domain=localhost; path=/; " + "expires=Mon, 07 May 2007 04:38:18 GMT" ) cookie_hash = Hash[*cookie_parsed.collect{|k,v| [k,v[0]] }.flatten] cookie_hash['name'] = 'page_color' cookie_hash['value'] = cookie_hash[cookie_hash['name']] request.cookies['page_color'] = CGI::Cookie.new(cookie_hash) post :change_color assert_response :success assert cookies['page_color'] assert_equal 'green', cookies['page_color'].value.first end This last test shows you the steps involved in transforming a cookie from a CGI::Cookie object to a string and back again. It is important to understand when to use cookies and when not to use them. By default, Rails sets a single session id cookie when a user starts to browse the site. This session is associated with a session object in your Rails application. A session object is just a special hash that is instantiated with every request and made accessible in your controllers, helpers, and views. Most of the time you don't want to set custom cookies; just add data or model IDs to the session object instead. This keeps the user from having too many cookies, and conforms with the standards and best practices of the HTTP protocol. See Also
|