1. Jul 3rd, 2006

    assert_select plugin for Rails

    logo.jpg

    Update: The new release of assert_select includes support for CSS pseudo classes (nth-child, first-child, empty). More details here. It also supports nested assertions for dealing with lists, tables and forms. Some examples here. I updated this post to use nested asserts.

    Update 2:A new release adds support for RJS.

    Update 3: assert_select is now part of Rails core. Also, cheat sheet available here.

    assert_select helps you write functional tests using CSS selectors.

    If you’re using HTML and CSS, you’re already familiar with CSS selectors. CSS selectors have a simple syntax that lets you pick parts of the page and apply styling. assert_select uses the same simple syntax, for testing the content of the page.

    You can test that selected element(s) exist, has specific text content, test number and order of elements, and a few more tricks.

    Here’s a simple example that asserts the page has the right title:

    det test_page_has_right_title
      get :index
      assert_select "title", "Welcome"
    end

    And a more complex one that tests a login form:

    def test_login_form_has_all_fields
      get :login
      assert_select "form[action=http://myapp/login] input" do |inputs|
        assert_equal 3, inputs.size
        assert_select "input[type=name][name=username]"
        assert_select "input[type=password][name=password]"
        assert_select "input[type=submit][value=Login]"
      end
    end

    You can also use css_select to pick specific elements and work with them. This example asserts that the page header has all the right links:

    HEADER_LINKS = [
      ["Main", "http://myapp/"],
      ["About", "http://myapp/about"],
      ["Login", "http://myapp/login"]
    ]
    
    def test_page_header_links
      assert_select "#header>ul>li>a" do
        HEADER_LINKS.each do |text, href|
          assert_select "a[href=?]", href, text
        end
      end
    end

    Here’s a few more examples:

    # Form includes four input fields
    assert_select "form input", 4
    
    # Page does not have any forms in it.
    assert_select "form", false, "Page must contain no forms"
    
    # Page has one link back to user's page.
    assert_select "a[href=?]", url_for(:controller=>"user", :id=>user_id),
                 :count=>1, :text=>"Back to page"

    The last example uses substituation. The question mark gets replaced with an argument, in this case the result of url_for. You can use strings and regular expressions.

    To install assert_select:

    ./script/plugin install http://labnotes.org/svn/public/ruby/rails_plugins/assert_select

    As always, code is under MIT and/or Creative Commons license. Enjoy!

    1. Jul 9th, 2006

      Write functional tests in Rails using CSS selectors

      [...] assert_select is a plugin by Assaf Arkin that allows you to use CSS selectors in your functional tests to check that certain HTML elements match your assertions. On the surface, this isn’t too far removed from using why’s Hpricot to do assertions on HTML, but in reality having the full power of CSS selectors available changes everything. Some examples: [...]

    2. Jul 9th, 2006

      InfoHatter Blog! :: assert_select Plugin Allows you to Run Funtional Tests on your .rhtml

      [...] Ruby Inside just posted an interesting tidbit about the assert_select plugin written by Assaf Arkin. This is a cool little plugin which allows you to run functional tests on your .rhtml pages. [...]

    3. Jul 9th, 2006

      TDD juice

      [...] Labnotes » Blog Archive » assert_select plugin for Rails [...]

    4. Jul 12th, 2006

      Labnotes » Blog Archive » The UI is the API: Scraping with Ruby

      [...] N.B. Someone asked if you could use scrAPI for testing Web UIs. Yes. But you might find assert_select easier to use. It shares the same code and style (pun intended) but geared towards test cases. Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages. [...]

    5. Jul 31st, 2006

      Doug

      Hey Assif,
      Great plugin. I ran into what might be a bug, though.

      When checking for a form’s existence like this:
      assert_select “form[action=/job/find_customer_account_number_by_name]”

      All is well, and it acts as expected. However, after that things start failing. For example, both if these lines work when used without calling that form’s assert_select before hand:

      assert_tag :tag => “table”, :attributes => {:id => ‘customer_account_numbers’}

      assert_select “#customer_account_numbers”

      But if I do make that first assert_select call, these two lines don’t work. For example, the last assert_select returns the following:

      No match made with selector #.
      is not true.

      Any ideas? This is in an integration test, if it makes a difference.

      Thanks!

    6. Jul 31st, 2006

      Doug

      Here’s the whole chunk, if it helps:

      # Hit the job index and get redirected, because we’re not logged in.
      get ‘/job/index’
      assert_redirected_to(:controller => ‘user’, :action => ‘login’)
      # Log in, and go back to the Job index.
      post_via_redirect ‘/user/login’, :user => {:login => ‘admin’, :password => ‘12345′}
      assert_select “form[action=/job/receive]”
      #assert_select “legend#create_job_legend”
      # Look up customer’s by name
      post ‘/job/find_customer_account_number_by_name’, :find_customer_account_number_by_name => {:name => ‘Meg’}
      assert_response :success
      #assert_tag :tag => “table”, :attributes => {:id => ‘customer_account_numbers’}
      assert_select “#customer_account_numbers”
      assert_select “legend#create_job_legend”

    7. Jul 31st, 2006

      Doug

      Whoops, sorry for the false alarm. Looks like Integration Tests are the wrong place for looking at HTML: http://lists.rubyonrails.org/pipermail/rails/2006-May/042620.html

      Thanks again for the plugin!

    8. Jul 31st, 2006

      Assaf

      Doug,

      I didn’t try it with integration tests, only functional tests.

      One tip, if you’re routes are tested, you might as well use url_for, so from your example:

      assert_select “form[action=?]“, url_for(:controller=>”job”, :action=>”receive”)

    9. Sep 3rd, 2006

      Gluttonous : assert_select included in core, assert_tag deprecated

      [...] In case you missed the changeset, Assaf Arkin’s assert_select has been included in EdgeRails. This officially marks the deprecation of assert_tag. [...]

    10. Sep 3rd, 2006

      Kamal Fariz

      Any chance you might want to use why’s Hpricot for parsing the CSS selectors? You’ll get to use XPath notation for selects for free too!

    11. Sep 3rd, 2006

      Assaf

      Kamal,

      assert_select supports CSS 3 selectors, which have a lot of functionality for selecting content in HTML. The expressions are easier to read than XPath and much better at handling HTML as content.

      Not to mention, those are the same CSS selectors I use when styling the page.

    12. Sep 5th, 2006

      a work on process » Rails flash tests deprecated?

      [...] Kevin Clark has posted on the deprecation of assert_tag in favour of assert_select, but I’ve yet to see any notes on this one. Looking in the actionpack CHANGELOG there’s no reference to the change, and there’s no documentation in the new home of those methods (actionpack/lib/actioncontroller/assertions/deprecated_assertions.rb) to suggest what we should use instead other perhaps than making use of the code used in those methods. eg: [...]

    13. Sep 13th, 2006

      Kenneth Kalmer

      Thanks Assaf, awesome stuff! Just a note for your other readers is that assert_select and arts cannot coexist in the same project… Didn’t bother to figure out why, just removed the plugin and off I went with assert_select.

      Keep up the good work

    14. Sep 13th, 2006

      Assaf

      Kenneth,

      Too bad, I like Arts. It has some overlap with assert_select but also assertions that do other interesting things. I’ll contact the author and see what we can do.

    15. Sep 13th, 2006

      Assaf

      Kenneth,

      I just tried assert_select with arts in the same project and I’m not seeing any problems, neither is Kevin. If you’re seeing any problems, let us know.

      For those who don’t know, Arts is a great plugin for testing your RJS.

    16. Sep 22nd, 2006

      Joe

      Can this work with integration tests, or just functional tests?

    17. Sep 22nd, 2006

      Assaf

      Joe,

      Good question. I haven’t used integration tests yet, but from what I know the get/post will return a response body, so it will work with assert_select.

    18. Sep 23rd, 2006

      Joe

      Assaf,

      I tried using it, however I get an error… Maybe you could try it on your end to see if it’s something that could be easily changed so it works with integration tests… Here’s the error I get in case this sheds any light:

      NoMethodError: You have a nil object when you didn’t expect it!
      The error occured while evaluating nil.body
      /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/test_process.rb:419:in `html_document’
      /Users/joe/dev/rails/quikauctions/stable1.0/config/../vendor/plugins/assert_select/lib/assert_select.rb:141:in `assert_select’
      /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/integration.rb:523:in `method_missing’
      test/integration/calendar_test.rb:74:in `test_gateway_change_clears_calendar’
      /usr/local/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/integration.rb:431:in `run’

    19. Sep 27th, 2006

      ctagg

      Just tried this, but it’s having some parsing my response. I’m using Google Maps (via ym4r_gm, for what it’s worth), and there’s a load of HTML in the where the info_windows are defined, including some divs. Obviously they’re not valid HTML — the W3c validator doesn’t like the VML hack for IE, apart from anything else — but not sure of a way around that. Suspect I’ll have to live with them being unvalid, but would be nice to be able to use assert_select. I’m using hpricot_test_helper at the moment (which is pretty good), but what with assert_select going into core…

    20. Sep 27th, 2006

      Assaf

      ctagg,

      There are several options for processing invalid HTML, including Tidy, Hpricot and Rubyfulsoup, all of which can clean it up.

      With scrAPI, I use Tidy to clean up the HTML before processing it, and it uses the same engine as assert_select. So you can look at the code here:
      http://labnotes.org/svn/public/ruby/scrapi/lib/scraper/reader.rb

      The parse_page method can take html_document and give you back an HTML node that you can pass to assert_select as the first argument.

    21. Sep 29th, 2006

      Aníbal Rojas

      Works like a charm but in Firefox (1.5.0.7) the code with the plugin installation is rendered truncated, and the folder should be script/plugin and not script/plugins.

    22. Sep 29th, 2006

      Assaf

      Aníbal,

      Thanks, I fixed the typo. I also found a way to wrap the long text so it doesn’t truncate:

      http://www.longren.org/2006/09/27/wrapping-text-inside-pre-tags/

    23. Oct 4th, 2006

      Assaf

      Joe,

      Sorry for the late reply, I’m working on another patch and I’ll include integration tests in it.

    24. Jul 18th, 2008

      SDR

      Hi im really a newbie.. cud u please tell me how to install the assert_select plugin if i downloaded the .rb file to my computer.

      F:\InstantRails-1.3a-win\InstantRails\ruby\lib\ruby\gems\1.8\gems\actionpack-1.12.1\lib\action_controller

      should i add the method in the test process ???

    25. Jul 18th, 2008

      SDR

      Its ok … i got it !

    26. Jul 19th, 2008

      Assertions for Synergy/DE — Chip’s Tips for Developers

      [...] would also be useful to develop something like Assaf’s assert_select for user interfaces, but that will take quite a bit of thought for [...]