[rspec-users] Message expectation that verifies result of passed block
Matt Wynne
matt at mattwynne.net
Thu Jan 28 17:45:50 EST 2010
On 28 Jan 2010, at 21:43, Nicolás Sanguinetti wrote:
> You're definitely testing too much implementation and not enough
> behavior.
>
> Basically, what you want to spec, is that provided some options, when
> you call a certain method of your form builder, you get a certain html
> output. At least that's how I would approach the problem.
>
> So I would have something like this:
>
> it "produces a correctly formatted FOO input" do
> html = @builder.foo_text(...)
> html.should == "<label for='foo'>FOO</label> <input id='foo'....>"
> end
>
> Since testing generated HTML like that sucks, I would proceed to use
> something like http://github.com/fnando/rspec-hpricot-matchers, and
> say
>
> it "produces a correctly formatted FOO input" do
> html = @builder.foo_text(...)
> html.should have_tag("label", :for => "foo")
> html.should have_tag("input", :id => "foo")
> end
>
> Or something like that. But then if you stop using that
> question(*args, &block) method, and refactor to a different
> implementation, specs should continue to pass.
What he said.
> -foca
>
> On Thu, Jan 28, 2010 at 7:14 PM, Paul Hinze <paul.t.hinze at gmail.com>
> wrote:
>> Ashley Moran <ashley.moran at patchspace.co.uk> on 2010-01-28 at 13:28:
>>>
>>> On Jan 28, 2010, at 1:29 pm, Paul Hinze wrote:
>>>
>>>> I believe the lack of ability to use this notation comes down to
>>>> a ruby
>>>> limitation, but I'm not sure. If that's the case, then we would
>>>> need a
>>>> specific argument expectation (along the lines of my suggestion)
>>>> that
>>>> executes in a context in which it can call the block.
>>>
>>> I can't find a solution, I suspect Ruby 1.8 can't do this, but I'm
>>> guessing.
>>>
>>> Can I ask why you want to do this though? As another example, it
>>> would be unusual to spec something like:
>>>
>>> @array = [1, 2, 3]
>>> @array.should_receive(:map).with(block_that_doubles_values)
>>>
>>> You'd instead check that the array that came out was [2, 4, 6].
>>
>> I'm trying to spec a large set of what essentially come down to
>> decorator methods in a Rails FormBuilder extension plugin. What this
>> boils down to is methods that wrap rails FormBuilder methods, so
>> `f.text(*args)` ends up calling `f.text_field(*args)` to generate an
>> <input> tag, but only after it does its own logic and wrapping, which
>> among a bunch of other things wraps the output in an <li>.
>>
>> So the methods run the gamut in complexity from 'f.radio' to
>> 'f.dependent_collection' to 'f.sigma', but much of the common code is
>> wrapped up in a method called 'f.question', which does the outer <li>
>> wrapping, required field detection, label and error display, and a
>> few
>> other common things required by every control we use in our forms.
>>
>> So most of our methods have this basic structure:
>>
>> class OurFormBuilder < ActionView::Helpers::FormBuilder
>> def foo_text(method, options={})
>> foo_option = options.delete(:foo_option)
>> options[:value] ||= 'FOO'
>>
>> # some logic, using foo_option somewhere...
>>
>> question(method, options) do |remaining_options|
>> 'FOO -->' + text_field(method, remaining_options) + '<-- FOO'
>> end
>> end
>> end
>>
>> I started out with a nice spec for `question`'s behavior and made
>> it all
>> in a shared group, but because of the number of examples just for
>> question and the number of methods that call it (so both
>> performance and
>> complexity), I'm thinking about switching to message expectations
>> in all
>> of my `foo_text`-style method specs:
>>
>> describe 'foo_text'
>> it 'calls text field with the proper options' do
>>
>> @builder.should_receive(:text_field).with(:some_method, :proper_args)
>> @builder.foo_text(:some_method)
>> end
>> it 'yields a wrapped text_field into question' do
>> # dont test rails text_field
>> text_field_return = "BOOGA"
>> @builder.stub!(:text_field).and_return(text_field_return)
>>
>> expected = "FOO -->" + text_field_return + "<-- FOO"
>>
>> @builder
>> .should_receive
>> (:question).with(:some_method).with_a_block_yielding(expected)
>>
>> @builder.foo_text(:some_method, :some => options)
>> end
>> it 'properly returns the result of the call to question' do
>> @builder.stub!(:question).and_return('BOOGA')
>> xhtml = @builder.foo_text(:some_method)
>> xhtml.should == 'BOOGA'
>> end
>> end
>>
>> I'd appreciate any feedback that folks might be willing to give.
>> Particularly I realize the following:
>>
>> (a) This might be testing implementation too much (possible)
>> (b) The architecture of the whole plugin needs a serious refactor to
>> increase modularity and decouple the components (very likely); a
>> dash of decent OO design could really help this whole situation,
>> and it's something I'm planning on tackling down the road
>>
>> For now, I'm just trying to push things in the right direction, and I
>> _think_ the .with_a_block_yielding(value)
>> or .with(block_yielding(value))
>> argument verification would help me do that.
>>
>> Thanks for your time!
>>
>> Paul
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
>>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
cheers,
Matt
http://mattwynne.net
+447974 430184
More information about the rspec-users
mailing list