Peter Marklund

Peter Marklund's Home

Thu Aug 24 2006 06:53:00 GMT+0000 (Coordinated Universal Time)

Rails Recipe: Checking for Broken Links in Integration Tests

Having a way to automatically check for broken links in your application can be quite valuable and there is a simple way to do that with integration tests. Here is the DSL methods that I used for that purpose along with some sample usage:

  module TestingDSL
    def visits_start_page      
      get_with_links("/mcm") do
        assert_tag :tag => 'a', :attributes => {:href => '/mcm/contacts/search_form'}
      end
    end

    def get_with_links(*args)
      request_with_links(:get, *args)
    end

    def post_with_links(*args)
      request_with_links(:post, *args)
    end

    def request_with_links(method, *args)
      self.send(method, *args)
      yield if block_given?
      assert_response :success
      check_links            
    end

    def check_links
      find_all_tag(:tag => 'a').each do |anchor|
        url = anchor.attributes['href']
        if check_url?(url)
          get(url)
          assert_response_code
        end
      end      
    end

    def check_url?(url)
      [/mailto:/, /javascript:/, /user\/logout$/, /:\/\//, /^\#$/].each do |skip_pattern|
        return false if url =~ skip_pattern
      end
      true
    end
    
    def assert_response_code
      assert [200, 302].include?(@response.response_code), 
        "Invalid response code #{@response.response_code} for path #{@request.path} #{@response.body}"
    end
  end

I've found this approach to link checking helps me find bugs that other tests don't find. The only downside is that it can significantly increase the number of requests in your integration tests and thus the test time. There are ways to work around that though, you don't have to check links on every page request, and the link checker could be extended to not request the same URL twice.