On Sun, Dec 16, 2012 at 11:34 AM, David Chelimsky <[email protected]> wrote:
> On Sun, Dec 16, 2012 at 3:07 AM, Perry Smith <[email protected]> wrote:
>> Below are two methods of doing the same thing in a view spec. I have some
>> hidden magic to tie in to capybara which I am not showing.
>
> First question: why lay another abstraction on top of what is already
> an abstraction for spec'ing html content?
>
>>
>> To me, the advantage of the first is if there is a failure, there will be a
>> very nice error message that tells you exactly what is wrong. The downside
>> of the first is it renders the template eight times instead of just once. I
>> can't figure out a way around that. "render" is not available until after
>> the "it" is entered. before(:all) didn't work when I tried it. The first
>> form also creates beautiful documentation when --format documentation is
>> used.
>>
>> Part of my question is this: I've heard that with unit tests, you want one
>> "assert" per test. This is what the first method does.
>
> The motivation for one expectation per example is that if the first
> expectation fails, the subsequent expectations are not evaluated. This
> can lead to a frustrating scenario in which you get the first
> expectation to pass, only to find that the second one fails. Then you
> get that one to pass only to find that the third fails, etc.
>
> The tradeoff is that, with one expectation per example, you end up w/
> more examples. Ideally you want to find a balance that gives you the
> best of both worlds: as few examples as are necessary to describe the
> behavior _and_ quick failure isolation when there is a failure.
>
> You can spec that a label with specific properties should exist in a
> single line w/ Capybara:
>
> # render returns @rendered, which is also returned by the rendered method.
> example { render.should have_selector("form label[for=swinfo]", :text
> => "item") }
>
> Given that, I don't see much benefit from the extra abstraction and
> redirection in the examples below. The error message could offer more
> info (it only says that it couldn't find a matching selector), but it
> turns out to be quite a challenge to do so in a way that works for all
> situations. If you have a good way to do it, please submit a pull
> request to Capybara for Jonas' consideration.
>
> Cheers,
> David
Answering your question about whether this level of granularity is
warranted, as with all things, it becomes a matter of tradeoffs. If
it's easy to write and maintain, of course! If it's not, then you
weigh the risks. It also depends on the risk associated with the
content failing. It can often have a bigger negative impact if
protected content is displayed to the wrong user than if
inconsequential content is NOT displayed. Make sense?
I haven't been doing any Rails for a bit, but I've done plenty over
the last several years. In most cases I drove out forms with coarse
examples using tools like Capybara. When there was some conditional
logic in the views (i.e. only show if user is admin, etc) I added view
specs, though eventually I got in the habit of using more declarative
methods with blocks:
<% admin_user do %>
<... protected content ...>
<% end %>
It's easy to spec that method in a helper spec (more focused than a
view spec), and the protected content can be spec'd to be absent in a
Capybara spec. That combination made it easy to catch bugs introduced
when moving view content around to partials, etc, and easy to pinpoint
the problem when that happened.
Hope to see some other opinions/approaches in this thread.
HTH,
David
>
>> The second method is very concise... but does not produce as nice an error
>> message and rather horrible output for --format documentation
>>
>> I'm also curious if rigorously testing a form like this is typical. I know
>> that I often screw up the forms and don't figure it out until I try to
>> exercise them.
>>
>> describe "welcome/index.html.erb" do
>> # first method
>> describe "the swinfo form" do
>> before(:each) do
>> render
>> end
>>
>> def form
>> @form ||= rendered.find('form#swinfo_form')
>> end
>>
>> def label
>> @label ||= form.find('label')
>> end
>>
>> def text_box
>> @text_box ||= form.find('input[type="text"]')
>> end
>>
>> it "should exist" do
>> form
>> end
>>
>> it "should have an action of /swinfos" do
>> form[:action].should == '/swinfos'
>> end
>>
>> it "shoule have a method of post" do
>> form[:method].should == 'post'
>> end
>>
>> it "should have a label" do
>> label
>> end
>>
>> describe "the label" do
>> it "should have text of 'swinfo'" do
>> label.text.should == 'swinfo'
>> end
>>
>> it "should be for 'item'" do
>> label[:for].should == 'item'
>> end
>> end
>>
>> it 'should have a text field' do
>> text_box
>> end
>>
>> describe "the text field" do
>> it "should have a name of 'item'" do
>> text_box[:name].should == 'item'
>> end
>> end
>> end
>>
>> # second method
>> it "the 'which filesets' form" do
>> render
>> within 'form#which_filesets_form' do |form|
>> form[:action].should == '/which_filesets'
>> form.should have_selector('input[type="submit"]')
>>
>> form.find('label').tap do |label|
>> label.text.should == 'which fileset'
>> label[:for].should == 'path'
>> end
>>
>> form.find('input[type="text"]').tap do |text_box|
>> text_box[:name].should == 'path'
>> end
>> end
>> end
>> end
--
You received this message because you are subscribed to the Google Groups
"rspec" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit https://groups.google.com/groups/opt_out.