Testing with CSS selectors
Posted by David July 30, 2006 @ 04:46 PM
If you have trouble wrapping your head around assert_tag and its options, you might want to give CSS selectors a chance instead. There are currently two plugins for handling that: assert_select and hpricot helper.
The hpricot style gives you something like assert element('title').should_contain('Funky Chicken Title') and the assert_select gives you something like assert_select "title", "Welcome".
From a brief cursory overview, I tend to like the assert_select style better. But I have yet to use either for real stuff. Please do shoot a comment with your experiences. One of these methods could well make it into core.

You know since the early days I’ve had this down and dirty trick that has worked well for me in functionals:
assert_not_nil @response.body.index(‘
Something I wanted rendered
‘)There might be something wrong with this, but it seems to work well for me.
David, a bit of background on assert_select.
I wrote the CSS logic for a service that extracts data from HTML pages, and used it in production for six months.
When I got lazy with assert_tag, I created assert_select using the same core library. Then evolved it over time to get full test coverage. I only recently extracted the code into a plugin and added a test suite. Currently tested against Rails 1.1.4.
In addition to CSS selectors, two features I found extremley useful are substitution and blocks. Substitution (use ? and pass actual value as argument) helps test against fixtures, and when you need regular expressions. Blocks keep you DRY when testing lists, tables, forms and anything else with structure and repetition.
It made my life simpler, let’s make life simpler for more Rails developers.
I’ve done a little run through on the hpricot helper on my site, but had never come across the assert_select one, I’ll make sure to do a comparable example some time. Thanks.
The hpricot helper’s tags method feels like assigns() and friends. I like that. I don’t care for its rspec style assertions, but they don’t have to be used and tags() can be used with general assertions easily.
Some of the assert_select syntax feels a little clunky.
eg. assert_select inputs0, “inputtype=name“
Kevin,
I pushed another update to assert_select today that makes it easier to test lists, tables, forms and other structured content using nested assertions.
So that takes care of the clunkiness. (I’ve been working a lot with lists recently, and felt the pain)
http://blog.labnotes.org/2006/07/30/assert_select-rails-core-and-handling-lists-and-tables/
The rest is just a matter of style.
Glad you like my plugin Jamie, I hadn’t seen the article on your blog.
There is currently some discussion going on about rolling one of these into the rSpec core, something I’d love to see as I generally prefer to use rSpec over Test::Unit…just waiting for that solid Rails support (which seems to be nearly there).
I’ve been using the Hpricot based plugin, works great for us. I dunno about assert select, but Hpricot lets you write things like:
Which I’m sure you can already see is pretty declarative. “Assert that the first name cell in the first users row contains “Joe””. Let’s me make sure that data is sorted the way I expected and the output is there. Also it encourages me to write “semantic” HTML.
I’ve been looking at both these solutions recently in pursuit of a painless view testing DSL. Both are execellent solutions.
I think that assert_select is probably a viable option for inclusion in Rails core, as it feels similar to the existing assertions and carries no outside dependencies.
The Hpricot helper will probably perform better, since its parsing logic is driven by the Hpricot C extension. I think this dependencies is probably going to relegate the Hpricot helper to plugin land permanently…
I would love to know if the controller/view testing (from ZenTest) is going to hit the core? Seems like a pretty good idea and has been working for me nicely. That way we really can have unit tests on the controllers and functional tests that test functionality instead of a slightly confusingly named mashup.