Brandon Olivares
2009-Apr-02 01:25 UTC
[rspec-users] Problem with Custom matcher and Blocks
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
On 2 Apr 2009, at 02:25, Brandon Olivares wrote:> 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>#have_selector is part of webrat. Have you required the appropriate files so that method is visible to your new matcher class?> 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 > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersAs 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
Brandon Olivares
2009-Apr-02 21:20 UTC
[rspec-users] Problem with Custom matcher and Blocks
Hi,> -----Original Message----- > From: rspec-users-bounces at rubyforge.org [mailto:rspec-users- > bounces at rubyforge.org] On Behalf Of Matt Wynne > Sent: Thursday, April 02, 2009 3:55 PM > To: rspec-users > Subject: Re: [rspec-users] Problem with Custom matcher and Blocks > > > #have_selector is part of webrat. Have you required the appropriate > files so that method is visible to your new matcher class? >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
On Thu, Apr 2, 2009 at 3:55 PM, Matt Wynne <matt at mattwynne.net> wrote:> > On 2 Apr 2009, at 02:25, Brandon Olivares wrote: > >> 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> > > #have_selector is part of webrat. Have you required the appropriate files so > that method is visible to your new matcher class? > >> 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 >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at 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. >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
Brandon Olivares
2009-Apr-02 21:56 UTC
[rspec-users] Problem with Custom matcher and Blocks
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
On 2 Apr 2009, at 22:20, Brandon Olivares wrote:> Hi, > >> -----Original Message----- >> From: rspec-users-bounces at rubyforge.org [mailto:rspec-users- >> bounces at rubyforge.org] On Behalf Of Matt Wynne >> Sent: Thursday, April 02, 2009 3:55 PM >> To: rspec-users >> Subject: Re: [rspec-users] Problem with Custom matcher and Blocks >> >> >> #have_selector is part of webrat. Have you required the appropriate >> files so that method is visible to your new matcher class? >> > > 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?Think so. Did the error go away?> 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 > endI 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
On 2 Apr 2009, at 22:21, Zach Dennis wrote:> 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,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
Brandon Olivares
2009-Apr-02 23:04 UTC
[rspec-users] Problem with Custom matcher and Blocks
> -----Original Message----- > From: rspec-users-bounces at rubyforge.org [mailto:rspec-users- > bounces at rubyforge.org] On Behalf Of Matt Wynne > Sent: Thursday, April 02, 2009 6:04 PM > To: rspec-users > Subject: Re: [rspec-users] Problem with Custom matcher and Blocks > > > 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?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
Brandon Olivares
2009-Apr-04 02:21 UTC
[rspec-users] Problem with Custom matcher and Blocks
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/thread/b0c08ffd06c0f9e6#m 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/tree/master 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> -----Original Message----- > From: Brandon Olivares [mailto:programmer2188 at gmail.com] > Sent: Thursday, April 02, 2009 7:04 PM > To: ''rspec-users'' > Subject: RE: [rspec-users] Problem with Custom matcher and Blocks > > > > > -----Original Message----- > > From: rspec-users-bounces at rubyforge.org [mailto:rspec-users- > > bounces at rubyforge.org] On Behalf Of Matt Wynne > > Sent: Thursday, April 02, 2009 6:04 PM > > To: rspec-users > > Subject: Re: [rspec-users] Problem with Custom matcher and Blocks > > > > > > 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? > > 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