
Working with feeds
Alphabetically that would be Atom and RSS, but I didn’t do much testing for the Atom code yet. If you’re using Atom and have some time to spare, have a look and let us know if it works.
Feeds are XML and there’s a lot of different ways to validate XML, and I bet CSS selectors is not the first thing that comes to mind. CSS is for HTML right? Actually, CSS selectors can handle more than just HTML. And when it comes to feeds, I find them simpler than XPath and just good enough. So let’s reuse those skills.
From the CSS 3 Selectors, W3C Working Draft 15 December 2005:
Selectors are patterns that match against elements in a tree. Selectors have been optimized for use with HTML and XML, and are designed to be usable in performance-critical code.
Since it’s XML, you can’t call assert_select on the feed itself, it chokes on link and other self-closing tags. Instead, start out with assert_select_feed and use nested assertions on the feed content. Like this:
assert_select_feed :rss, 2.0 do
assert_select "channel" do
assert_select "item" do
. . .
end
end
end
Works like a charm until you hit the description. It happens to be HTML encoded inside XML, which gives us three options:
- Ignore it. I did that until last week, when I realized a copy & paste mistake landed me with invalid HTML inside the feed.
- String comparison. Time to bring back regular expressions?
- Un-encode it back to HTML and run
assert_selecton it.
Here’s what it looks like:
# Start with item description
assert_select "item>description" do
# Unencode it to HTML
assert_select_encoded do
# Assertions on the description
assert_select "p", "This is my feed item"
end
end
Along with RSS/Atom support, I also rolled out a fix for a bug with :first-of-type and :last-of-type, and a fix that allows multiple :not pseudo selectors (see example below).
assert_select tips
Here’s a few things you can try yourself, extracted from my test cases.
Let’s make sure the page has the right header, and only one header. Sounds like I’m testing too much? Not as much as I’d like, but after one copy & paste error led me to duplicate the header on a page, I decided to be defensive and add a test case for that page. As the Raven says, “nevermore”.
assert_select "h1", :count=>1, :text=>"Page header"
Using assert_select without a count will do the right thing and make sure you have at least one element that matches the assertion. I added the count to make sure there’s only one element like that.
For pagination, I have a link to the previous page, next page, and at most five numbered pages:
assert_select "#pagination a.previous", "Previous «" assert_select "#pagination a.next", "» Next" assert_select "#pagination a:not(.next):not(:previous)", :maximum=>5
Notice the double :not pseudo class to pick all the links that are not previous or next. :not(.next.previous) will get you different result, I’ll leave it as an exercise to figure out the difference.
Dealing with paragraphs with different values:
assert_select "p" do |elements| assert_select elements.first, "p", /First paragraph/ assert_select elements.last, "p", /Last paragraph/ end
Pseudo selectors would work just as well:
assert_select "p:first-of-type", /First paragraph/ assert_select "p:last-of-type", /Last paragraph/
And last tip for today, here’s a paragraph:
<p> <em>"This is <strong>not</strong> a big problem,"</em> he said. </p>
And the assertion:
assert_select "p", "\"This is not a big problem,\" he said"
The text is extracted from the tags, so there’s no need to do nested assertions for simple cases like that.
But if you do want to test the HTML without nesting assertions:
assert_select "p", "<em>\"This is <strong>not</strong> a big problem,\"</em> he said."