Thank you for your answers!
In The Rspec Book, section 24.6 (chapter 24), “When I write view specs”
offers some tips for determining when to write view specs. Have you read
this section?
I though I did, but I revisited it and I learned some new things.
Thanks!
I did some thinking on my own and I reached some conclusions. I would
like
to share them – it will help me structure my reasoning. I would be
happy to
hear what you think. This is going to be long.
I’m assuming that I want to use test doubles in view specs. I don’t see
much
value in testing model, view and helper code at once. Also, I’m only
checking for the presence of text and occasionally for the presence of a
link. I’m not asserting DOM structure or rendering order.
There is a big difference between using doubles in controller specs and
in
view specs. Controllers depend only on the models. Using test doubles in
their specs leads you to a nice, clean public interface of the models
and
only a handful of simple messages in the actions. The code is just a
simple
moderator. This is what a clean controller should be, in my opinion.
Views, on the other hand, know a lot of tiny details about the models.
After
all, they should if they are to present that information in a humane
way.
Thus, they are more coupled to the models than controllers. This
requires
more intricate test doubles. Furthermore, they also invoke helpers to do
some of the work. Not only you have to mock them too, but most of the
time
helpers just take a model and return a result dependent on it. This is
tricky to mock (simplest case you make a mock of the model and set an
expectation on a helper).
That’s two problems with test doubles in view specs – (1) view and
model is
more tightly coupled and (2) you have helpers as a depend-on component.
At
this point, getting the test doubles right is too much work. The usual
problem that I have, is that as soon as I complete a refactoring in the
model, I have to spend an equal (or even greater) effort to fix the
doubles
in spec failures, that are false negatives. I think I’ve seen this named
‘fragile test’.
Thus, my approach would be to avoid writing view specs most of the time
and
make sure I’ve provided the safety net with Cucumber. That is my case
against view specs.
I can see two reasons to write them, though.
First, I can consider it as purchasing technical debt. I write a view
spec
instead of a Cucumber scenario for a small behavior modification. This
gives
me the safety net for a lower price (view specs take considerably less
time), but the cost is that it hinders refactoring.
Second, if I have something complex to display and no idea now to
organize
it, I can use a view spec to drive the design. This let’s me base it on
feedback, but when I’ve implemented the functionality back-to-back, the
view
spec has turned into an expensive artifact that hinders further
refactoring.
I’ve done that a few times – it really helps me with the design, but I
find
myself removing the spec after a while since it takes just too much time
to
maintain.
I also see two things that might invalidate this reasoning.
First, presenters. They group the model and helpers logic that the view
depends on, so now I would have to mock only one object. More
importantly,
that object is specifically designed to be used by the view, which means
that the test double will be simple and that I can use the view spec to
drive the design of the presenter.
Second, moving all logic from the view to helpers that take the model as
a
parameter. This should simplify the test doubles, although you still
will
have to set expectations on helper invocations. This moves the intricate
knowledge of the models in the helpers, which are easier to test. The
view
mediates models and helpers.
I’m not sure if I like the resulting design. While the views look slick,
you
have to understand the helpers first in order to understand the view.
Logic
that belongs together is scattered around in multiple files,
intermingled
with other logic. Also, at this point I don’t see much reason to write a
view spec either, since the code that could possibly break is either in
models or helpers, which are tested in isolation. If you insist on doing
it,
setting up test doubles is still a bit weird (you have to set
expectations
and define results on helpers and you have to use mocks for models). It
gets
particularly tricky when the view iterates over a collection and calls a
few
helpers with each item.
On the more general note, while controllers are pretty OO in they nature
and
thus can tolerate test doubles, views are, well, just procedural Ruby
embedded in HTML. Applying mocks to them doesn’t seem a good fit to me.
So, what do you think? Does this make sense? Did I miss anything?