I had wanted to do a decent job writing BDD tests for my views. I was a bit surprised that the matchers were using the string form of the HTML. I converted the string to a Capybara node in order to use the Capybara matchers.
HTML is tree-structured and my specs mirror the HTML structure. I check the parent node for correctness and then verify the children. I had thought Capybara supported hierarchical specs using "within" but that doesn't seem to work for views. It could be cockpit error on my part but Google searches didn't show any better way.
So I spent a little time adding to the RSpec DSL. I wanted to have describe "scope into a subnode" of the current subject (the current subject being the equivalent of a DOM node). I named the DSL "with_subnode". With_subnode acts like describe with the ability to change the subject to a subnode.
The current subject is assumed to be a Capybara node. with_subnode takes in the name of a Capybara::Node method (op:) to apply to the current subject plus any arguments (args:)
One starts by constructing a root_node using let( :root_node) do ... end. This should be the string form as present in the rendered attribute.
The with_root_node method converts the root_node string into a Capybara::Node::Simple and makes it the subject of the do...end.
HTML is tree-structured and my specs mirror the HTML structure. I check the parent node for correctness and then verify the children. I had thought Capybara supported hierarchical specs using "within" but that doesn't seem to work for views. It could be cockpit error on my part but Google searches didn't show any better way.
So I spent a little time adding to the RSpec DSL. I wanted to have describe "scope into a subnode" of the current subject (the current subject being the equivalent of a DOM node). I named the DSL "with_subnode". With_subnode acts like describe with the ability to change the subject to a subnode.
The current subject is assumed to be a Capybara node. with_subnode takes in the name of a Capybara::Node method (op:) to apply to the current subject plus any arguments (args:)
One starts by constructing a root_node using let( :root_node) do ... end. This should be the string form as present in the rendered attribute.
The with_root_node method converts the root_node string into a Capybara::Node::Simple and makes it the subject of the do...end.
The example:
The code:
The code looks a bit odd. That has to do with the way RSpec builds tests. Each example/example_group is a meta-programmed class and one loses the lexical scoping. Each with_subnode clause is assigned a unique symbol. That symbol is used to retrieve the Capybara subject node from an associative array.
Unique symbols with an external associative array are used instead of let(). let() order evaluation along with inadvertent infinite recursion made that solution problematic.
The with_subnode clause is converted into two describe clauses. The inner describe clause is what the user placed in the do...end. The outer describe clause establishes the new subject.
An extra "it should_not be_nil" clause is added after the subject clause. This clause ensures the subject clause will be evaluated before the inner describe clause.
Unique symbols with an external associative array are used instead of let(). let() order evaluation along with inadvertent infinite recursion made that solution problematic.
The with_subnode clause is converted into two describe clauses. The inner describe clause is what the user placed in the do...end. The outer describe clause establishes the new subject.
An extra "it should_not be_nil" clause is added after the subject clause. This clause ensures the subject clause will be evaluated before the inner describe clause.
No comments:
Post a Comment