Forum: RSpec Problem with Custom matcher and Blocks

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (Guest)
on 2009-04-02 03:57
(Received via mailing list)
Hi,

I'm trying to write my first custom matcher.

Here's a bit of my example group.

describe "/contact/index" do
  include FormMatchers

  before(:each) do
    render 'contact/index'
  end

  it "should show the contact form" do
    response.should have_a_contact_form
  end

  describe "the contact form" do
    context "before it has been submitted" do
      it "should have a subject dropdown box" do
        response.should have_a_contact_form do |form|
          form.should have_a_subject_field
        end
      end # it "should have a subject dropdown box"

      # This should be failing
      it "should have a name field" do
        response.should have_a_contact_form do |form|
          form.should have_a_name_field
        end
      end # it "should have a name field"

      describe "the subject dropdown box" do
        it "should have a feedback option" do
          response.should have_a_contact_form do |form|
            form.should have_a_subject_field do |subject|
              subject.should have_selector('option', :content =>
'Feedback')
            end
          end
        end # it "should have a feedback option"

        # Etc...

      end # describe "the subject dropdown box"

    end # context "before the form has been submitted"

  end # Describe "the contact form"

end # describe "/contact/index"

Right now, everything before the describe "the subject dropdown box" is
passing, even though the one testing the name field should not because
I've
not added that field yet.

When it gets to the describe block for the subject, I get:

NoMethodError in '/contact/index the contact form before it has been
submitted the subject dropdown box should have a blank option'
undefined method `have_selector' for
#<FormMatchers::HaveAFormWithID:0x7f3260ac>

The have_a_contact_form method is as follows:

  def have_a_contact_form &block
    have_a_form_with_id 'contact', &block
  end

That calls a have_a_form_with_id method, which calls the haveAFormWithID
class.

module FormMatchers

  class HaveAFormWithID

    def initialize id, &block
      @id = id
      @block = block
    end

    def matches? response
      response.should have_selector('form#%s' % [@id]) do |form|
        !@block or @block.call form
      end
    end

    def description
      "have a form with id #{@id}"
    end

    def failure_message
      "expected to have a form with ID #{@id}"
    end

    def negative_failure_message
      "expected not to have a form with ID #{@id}"
    end

  end

  def have_a_form_with_id id, &block
    HaveAFormWithID.new id, &block
  end

end

Sorry for all the code. Again this is my first custom matcher, so I
could be
doing something very wrong.

Any help much appreciated.

Thanks,
Brandon
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-04-02 22:48
(Received via mailing list)
On 2 Apr 2009, at 02:25, Brandon Olivares wrote:

>    render 'contact/index'
>          form.should have_a_subject_field
>      describe "the subject dropdown box" do
>
> passing, even though the one testing the name field should not
> because I've
> not added that field yet.
>
> When it gets to the describe block for the subject, I get:
>
> NoMethodError in '/contact/index the contact form before it has been
> submitted the subject dropdown box should have a blank option'
> undefined method `have_selector' for
> #<FormMatchers::HaveAFormWithID:0x7f3260ac>

#have_selector is part of webrat. Have you required the appropriate
files so that method is visible to your new matcher class?

> module FormMatchers
>        !@block or @block.call form
>
> end
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users


As a P.S. I hate to take the wind out of your sails here, but I was
reflecting only today how, after 9 months of using RSpec to TDD our
Rails app, we have massaged the view specs down to almost nothing. IMO
99% of the time you should be writing a Cucumber feature instead and
leaving it at that.

Matt Wynne
http://beta.songkick.com
http://blog.mattwynne.net
F86901feca747abbb5c6c020362ef2e7?d=identicon&s=25 Zach Dennis (zdennis)
on 2009-04-02 23:24
(Received via mailing list)
On Thu, Apr 2, 2009 at 3:55 PM, Matt Wynne <matt@mattwynne.net> wrote:
>>  include FormMatchers
>>   context "before it has been submitted" do
>>       end
>>
>> Right now, everything before the describe "the subject dropdown box" is
>
>> class.
>>   def matches? response
>>     "expected to have a form with ID #{@id}"
>>  end
>> Brandon
> time you should be writing a Cucumber feature instead and leaving it at
> that.
>

To share, one project I'm on has had scenarios provided by the
customer and it is an app with a lot of business rules and the system
contains many components (not a typical Rails CRUD app). The scenarios
themselves focus on high level behaviour. We've used view specs to
drive out the views, and we've only placed the minimal code in the
steps to use the system to prove out the behaviour. Relying solely on
scenarios and step definitions did not work well for the short time it
was tried.

On another project that is entirely different the scenarios themselves
actually fleshed out the requirements of the UI. But the type of app
is vastly different than the first and it just made sense.

While I don't agree that 99% is a rule of thumb I can see where
different apps have different needs, and some will naturally drive out
more of the UI from the scenarios, whereas others will have details to
the UI which may not be the driving force of the feature and its
scenarios, but they need to exist.

I know that style comes into play at some point to. There are some
folks who are cool with putting all view stuff in step definitions and
there are folks who prefer to have minimally sized step definitions
with the details in specs. I can't say who's right or wrong, but I
know where my comfort level is.

Matt, can you say where you gauge your app?
 - Are there many little scenarios covering the details of the page?
 - Are the scenarios comprised of many steps which look at all aspects
of the page?
 - Are the step definitions detail oriented? e.g.: looking at the
project looks at all of the pieces of information it should be
displaying

I'm really interested in this topic as I think I've seen when Cucumber
is relied on too much and I've definitely seen where it's been relied
on too little. So naturally I'm interested to find out more about
people's projects to help gauge where things fall on the continuum,

--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (Guest)
on 2009-04-02 23:48
(Received via mailing list)
Hi,

>
Oops, forgot that.

OK, so I added:

Require 'webrat/core/matchers/have_selector'

On the first line, and directly within the class:

Include Webrat::Matchers

Is this correct?

OK, so now all 16 examples pass. But one of them should be failing --
the
one with the name. Do you see any reason that is still passing?

Again my matches? Method is as follows:

    def matches? response
      response.should have_selector('form#%s' % [@id]) do |form|
        !@block or @block.call form
      end
    end

> As a P.S. I hate to take the wind out of your sails here, but I was
> reflecting only today how, after 9 months of using RSpec to TDD our
> Rails app, we have massaged the view specs down to almost nothing. IMO
> 99% of the time you should be writing a Cucumber feature instead and
> leaving it at that.

Yeah, I'm really confused about that. I thought I've read from some
people
that they recommend doing Cucumber, but also view specs, and controller
and
model specs to test the different layers. Others say they don't test the
view with specs, so I don't know. I'm doing it for now and see how I
feel
about it.

Thanks,
Brandon
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-04-03 00:34
(Received via mailing list)
On 2 Apr 2009, at 22:20, Brandon Olivares wrote:

>> #have_selector is part of webrat. Have you required the appropriate
>
> Include Webrat::Matchers
>
> Is this correct?

Think so. Did the error go away?

>    end
I don't think you want to be using #should here. If that fails it will
raise an exception but the expected behaviour of a matcher is to
return true / false from #matches? so that will be one problem you
have. Try just hard-coding the #matches? method to return false and
see if you get one of your tests to fail.

What are you imagining that #response is going to return in this
instance?

Matt Wynne
http://beta.songkick.com
http://blog.mattwynne.net
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-04-03 00:58
(Received via mailing list)
On 2 Apr 2009, at 22:21, Zach Dennis wrote:

> on too little. So naturally I'm interested to find out more about
> people's projects to help gauge where things fall on the continuum,

What we have are a relatively small number of pages that have a lot of
different stuff on them, and that stuff depends a lot on the context.

So for example, a Concert page will show media about that concert, but
we juggle the media around depending on what there happens to be -
we'll prefer to show a poster if someone has uploaded one, but
otherwise we'll show a photo if there's one of those. Then, depending
on the aspect (landscape / portrait) of the photo, we'll use a
different grid layout for the media and show different numbers of them.

All these little rules get complex pretty quickly, and there are two
advantages I've found for expressing them in Cucumber.
(1) They're super easy to read for both the CSS / markup guy who
fiddles with the layout, the product owner, and anyone else who wants
to get a handle on them
(2) They're totally de-coupled from the implementation, which means I
can start with some ugly thing that filters and juggles arrays, while
we figure out exactly how we want it to work, then later optimize it
into a database query once the requirements have solidified.

This is the key thing for me about driving everything out from
acceptance tests - you get absolute flexibility about your
implementation with the confidence to know you can change whatever you
like and you're still covered. The trade-off I guess is the length of
the build, and possibly the maintainability of the test suite if
people get sloppy about keeping it tidy. The first is definitely an
issue for us now, but the second is not really a problem at all for us
as yet - 910 scenarios and counting..! :)

So to answer your questions, we would parts of the behaviour of each
page on the site as different features - so I guess this maps to what
you call 'many little scenarios covering the details of the page'

If you email me directly I'll send you a beta invite and you can take
a look for yourself :)

Matt Wynne
http://beta.songkick.com
http://blog.mattwynne.net
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (Guest)
on 2009-04-03 01:14
(Received via mailing list)
Hi again,

Well, I looked in the Webrat matchers code, and saw they also have a
block
argument to the matches? Method, though I don't know why. So I copied
what
they did there, and got a bit more informative output. Unfortunately now
I
have more errors.

Before I continue, do you know why I have to include &block as the
second
argument to `matches?'?

So now it won't recognize my subject field. The error is something like
this:

'/contact/index the contact form before it has been submitted the
subject
dropdown box should have a feedback option' FAILED
expected following output to contain a <select[id=subject]/> tag:
<form action="/contact.html" id="contact" method="post">
  <div>
    <label for="subject">Subject</label>
    <select id="subject" name="subject"><option value=""></option>
<option value="feedback">Feedback</option>
<option value="questions">Questions</option>
<option value="suggestions">Suggestions</option>
<option value="other">Other</option></select>
</div>
</form>
./spec/views/contact/index.html.erb_spec.rb:39:
/home/Brandon/projects/bartenders-to-go/spec/views/matchers/form_matchers/ha
ve_a_form_with_id.rb:16:in `call'
/home/Brandon/projects/bartenders-to-go/spec/views/matchers/form_matchers/ha
ve_a_form_with_id.rb:16:in `matches?'
/home/Brandon/projects/bartenders-to-go/vendor/gems/webrat-0.4.3/lib/webrat/
core/matchers/have_xpath.rb:21:in `call'
/home/Brandon/projects/bartenders-to-go/vendor/gems/webrat-0.4.3/lib/webrat/
core/matchers/have_xpath.rb:21:in `matches?'
/home/Brandon/projects/bartenders-to-go/spec/views/matchers/form_matchers/ha
ve_a_form_with_id.rb:15:in `matches?'
./spec/views/contact/index.html.erb_spec.rb:38:

That example is something like:

        it "should have a feedback option" do
          response.should have_a_contact_form do |form|
            form.should have_selector('select', :id => 'subject') do
|subject|
              subject.should have_selector('option', :content =>
'Feedback')
            end
          end
        end # it "should have a feedback option"

Thanks for any help.

Brandon
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (Guest)
on 2009-04-03 02:13
(Received via mailing list)
> return true / false from #matches? so that will be one problem you
> have. Try just hard-coding the #matches? method to return false and
> see if you get one of your tests to fail.
>
> What are you imagining that #response is going to return in this
> instance?

Oh OK. Well what I want is to be able to test if that form exists on the
page.

So how would you recommend going about it otherwise? I tried putting a
begin
... end block around it, but that doesn't seem to work either.

    def matches? response, &block
      @block ||= block
      begin
        response.should have_selector('form#%s' % [@id]) do |form|
          !@block or @block.call form
        end
      rescue
        false
      else
        true
      end
    end

It still has an error, but now it's covered up by my failure_message. I
output $! As a test, though, and it's the same error.

Brandon
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (Guest)
on 2009-04-04 04:24
(Received via mailing list)
Hi,

I've still been working on this, and can't figure it out.

I did see this old thread that covers a similar problem down the page a
bit:
http://groups.google.com/group/rspec/browse_thread...
sg_25d4f7ccf7362853

The OP eventually links to a project where it explains how to do what he
was
trying to do, which is here:
http://github.com/yura/howto-rspec-custom-matchers...

So I looked through, and it seems promising, but it's still not working.

I mean that the difference is that he is doing something like:

response.should have_form('/users') do
with_text_field 'First name', 'user_first_name'
end

Etc. But I want to pass a variable to the block with the scope, so I
want to
do something like:

response.should have_form('/users') do |form|
form.should have_text_field 'First name'
end

I find that more readable.

So does anyone know how I can do this?

Also, in this person's code, he passes a scope to the matcher, like:

Def have_form(action, &block)
HaveForm.new action, self, &block
End

And then he uses it in matches? Like:

response.should @scope.have_tag('form[action=?]', @action) do |form|
@block.call if @block
end

So I'm stumped. Any help would be much appreciated, as I've been working
on
this for at least three days now.

Thanks,
Brandon
This topic is locked and can not be replied to.