Testing from the view in Rails
I've been playing with Ruby on Rails for a few days and I must say it's been and still is a great experience. I like the focus on automation, for example by providing the necessary facilities to run unit and functional tests. I've taken the view centered approach in writing some of my functional tests (by means of the REXML-library), because that's one thing that in my experience has been hard to do in other environments. My first reactions are that
- it's great to get instant feedback on your actual output, as REXML screams at you if you feed it invalid XML, and
- the tests you write tend to be long.
I don't like the second point. Tests should be kept short and simple. Tests should be easy to write and easy to read. A lot of short, simple tests is better than a few big, complex ones.
I suspect the main reason my tests got big is that REXML requires me to verify not only the existence of specific elements but also their location within the XML output. That's not good because changing the layout doesn't necessarily mean changing the behaviour of the app. What I want to verify is that there is a div in my document with certain properties
and there is a certain kind of link somewhere inside that div
and so on. This is what I came up with to solve the problem:
module XPathExtensions
def find_elements(xml=nil, tag_spec=nil)
matched = Array.new
e = REXML::XPath.match(xml,tag_spec)
matched.concat(e)
REXML::XPath.each(xml) { |each|
arr = REXML::XPath.find_elements(each,tag_spec)
matched.concat(arr)
}
matched
end
end
I saved the code above in a file called lib/xpath_extensions.rb (I have some unit tests for it as well) and appended the following code to test/test_helper.rb to load the module.
require 'xpath_extensions'
REXML::XPath.extend(XPathExtensions)
Now I can simply call REXML::XPath.find_elements(xml, "a") to get back an array of all links in xml. I'll probably add a few assertions based on this method as well, perhaps something like assert_num_elems_matching, assert_elem_in_elem and so on, which would bring us down to one-liners to verify the existence of specific elements in the output.
I'm not a Ruby expert so the above code might not be the greatest. For one thing, I suppose it's a bit slow. Also, I haven't included the namespace parameter (mainly because I don't use namespaces in my output). It should be a trivial task to add. Finally, there might be ways to accomplish what find_elements does using nice Ruby one-liners or some feature of REXML that I'm missing. I just don't know how.
