From: Phlip Date: 2009-04-04T13:39:29+09:00 Subject: Re: [Ann] Verify, a very basic testing tool. Tom Cloyd wrote: > Damn. I love this sudden burst of creativity around the topic of > testing. A Good Thing. Oookay. Here's a sneak preview of assert{ 2.0 } 0.4.8. You know how Ajax works by generating JavaScript, and slinging it at your web browser? And you know how Rails purportedly tests it with "assert_rjs"? Here's a sample: assert_rjs :replace_html, "advanced_filter", "" Too cute, right? Wrong! That expands to nothing but a big Regexp, like /Element.update.*advanced_filter/. So a payload of "advanced_filter", or even a subsequent Element.update('advanced_filter'), could fool it. Further, at work we do a lot of in-house Ajax, so we are at liberty to render entire partials at whim. We require our teeming minions to use only Firefox. But all assert_rjs does with its third argument is drop it into assert_match. That is not powerful enough to constrain our apps! Just now while writing this post, I got Aaron Paterson's rKelly working in an assert_rjs clone. rKelly uses racc to parse and evaluate JavaScript. This matches our goal of _unit_ testing soft targets. Watir, Selenium, etc. are all great, and they introduced a generation to testing in general. Buuuuuut they work thru the browser. We are not inventing Ajax itself; we just need to accurately spot-check that our own data go into the correct slots in our JavaScript payloads. So here's a test that simulates a Rails functional test with xhr :get : @response = OpenStruct.new(:body => "Element.update(\"label_7\", \"I want a pet < than a chihuahua\");") assert_rjs :replace_html, :label_7 K, so far that looks like the original assert_rjs. But under the hood, it actually lexed the Element.update() call: ast.pointcut('Element.update()').matches.each do |updater| updater.grep(RKelly::Nodes::ArgumentsNode).each do |thang| div_id, html = thang.value if target and html div_id = eval(div_id.value) html = eval(html.value) if div_id == target.to_s assert_match matcher, html (Open question to Aaron - is that the best way to run the query?) The test actually determines we really got hold of the Element.update('label_7', ...). No other JavaScript line will match. Here's the assertions to match the text payload: assert_rjs :replace_html, :label_7, /Top_Ranking/ assert_rjs :replace_html, :label_7, /pet < than a chihuahua/ Ho hum; so far assert_rjs Classic could have done all that. But... Because I have an exact string, not a rough match, I can now treat it as pure HTML, and I can drop it into the mighty assert_xhtml()! Now the assertion looks like this: assert_rjs :replace_html, :label_7 do input.Top_Ranking! :type => :checked, :value => :Y input.cross_sale_1, :type => :hidden, :value => 7 end From here, no matter how complex that rendered partial, the assertion can keep up with it, and help make it safe to refactor and upgrade. BTW Verify and Testy can get on board if they A> import Test::Unit::Assertions (like certain other test rigs we could mention should), and B> implement flunk(). Both of those are all a custom assertion should ever need... -- Phlip http://www.zeroplayer.com/