[rspec-users] Ordering in view specs using have_tag and with_tag
win at wincent.com
Tue May 22 07:48:42 EDT 2007
El 22/5/2007, a las 2:27, David Chelimsky escribió:
> You don't have to know too much about Rails internals. assert_select
> is nicely decoupled from most of it. You will have to put on your
> regexp fu.
> Take a look at actionpack/test/controller/selector_assertions.rb and
Ok, I've had a look at the both the Rails code in actionpack/lib/
action_controller/assertions/selector_assertions.rb and the RSpec
implementations of have_tag and with_tag in lib/spec/rails/matchers/
doesn't exist in either Rails 1.2.3 or trunk; did you mean actionpack/
I don't necessarily have it fully grokked yet, but my observations of
the RSpec side of things thus far are:
1. Each time you invoke "have_tag" you create a new AssertSelect
instance under the covers
2. "with_tag" invokes "have_tag" behind the scenes, so that means a
new AssertSelect instance gets created there two
3. I presume the method that ends up getting called when you do
"should have_tag" is AssertSelect#matches?
4. If and only if the response text is a String,
AssertSelect#matches? creates a new HTML::Document instance from it
and prepends it to the arguments list
5. Finally, AssertSelect#matches? sends an "assert_select" message to
the @spec_scope (which is really "self", ie. the matcher).
So that's the way things are by the time Rails receives the message.
Observations on the Rails side:
1. It appears that assert_select and friends are included into the
object being tested via actionpack/lib/action_controller/assertions.rb.
2. assert_select uses an instance variable, @selected, to allow
nesting of assert_select; so perhaps using instance variables may be
the way to preserve state between assert_select calls and track the
ordering of the elements.
The only problem is, as I said I haven't fully grokked this yet so I
am not sure what the lifetime of the objects in question are and how
long the instance variables will stick around. It seems that a new
AssertSelect instance is created for every assertion, every assertion
has its own matcher, and the matcher receives the assert_select
message, so I am not sure how to achieve the kind of inter-matcher or
inter-assertion dependency which would allow ordering comparisons to
In other words, I don't know if this can be tackled purely from the
Rails side of things. One thing which may prove useful is that
assert_select returns an array of matches, and also passes that array
into any block (if called with a block). The array contains
HTML::Node instances, and these nodes have a position attribute (the
position of the node within the byte stream); so making comparisons
of order externally is possible and could be down already right now
with no changes (although not transparently; you'd have to explicitly
compare element positions in your specs).
There are two more questions that grow out of this:
1. What would be the ideal "scope" for these order restrictions to
take effect? Should multiple have_tag calls within the same example
(the same "it" block) be expected to appear in order? Or should it
apply to have_tags calls within the same context (the same "describe"
block)? Should calling "render" again have the effect of "resetting"
2. If this can be implemented, should it apply as a default (might
break existing specs), or should it be made optional (pass an
additional parameter to indicate that ordering is important?); an
alternative would be to add two additional methods, have_tags and
with_tags, which would expect an array of selectors rather than a
single selector, and would expect them to succeed in order (I almost
think that this is the best solution).
More information about the rspec-users