[TFUI] asserting HTML by example
2009-03-12 05:27:56 GMT
HTML is easy to test and hard to test well. A unit test must intercept and parse
it, hopefully _before_ it goes thru a web server.
I just wrote an assertion which turns that problem on its head. Instead of
asserting how to parse that HTML, we will assert how to build an example of HTML
that the production HTML must match.
This post is the high-level, language-agnostic, overview of these announcement
Here's the example:
user = users(:Moses)
get :edit_user, :id => user.id # fetches a web page
form :action => '/users' do
legend 'Personal Information'
label 'First name'
input :type => 'text',
:name => 'user[first_name]'
:value => user.first_name
That's all. Inside the assertion's block, you declare the HTML you want, using a
Builder notation. (Nokogiri, in Ruby's case.)
The assertion skips over any intervening elements, such as <ul>, <table>, etc,
that the block did not build. In the above example, <fieldset> only needs to
appear somewhere inside the <form> - not necessarily its first element. But
those <label> and <input> fields must appear somewhere inside that <fieldset>,
and in the given order!
To create this assertion in your language, start with your nearest HTML builder.
Allow the user-programmer to build an example, then traverse its DOM, and
convert each item into an XPath seeking that item in the production HTML.
Declare a match if all the specified attributes match, and if most of the text
matches - disregarding blanks.
Then, for each match, recursively match each built child element, applying its
XPath relative to the current node in the production HTML's DOM.
When you find a fault, build an error message by rendering the current nodes to
HTML, and return this out of your loop statements. This Gist explicates the
The algorithm essentially matches two trees, depth-first and in-order. The
example HTML must be a subset of the production HTML.
If you survive all that recursing and matching then your assertion passes. You
verified a huge number of details, using extremely lean code, and you ignored
many more details, allowing them to change freely as you upgrade your website.