Forum: RSpec Custom Example Groups

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 (devbanana)
on 2009-04-19 08:41
(Received via mailing list)
Hi,

I want to build a custom example group, but there really aren't any
examples
anywhere for how to do so. The new chapter in The RSpec Book talks about
it,
but doesn't actually show an example, only how to register it.

Looking at rspec-rails, I can see a pattern, but I don't know if that's
specific to rspec-rails, or if it is what should be done. In
rspec-rails, it
has:

Class << self

Within the class, and all the methods are within that.

So, are there any examples anywhere for how to write an example group?

Also, if I set an instance variable within the example group, will the
matchers specific to that group be able to access it? That'll simplify
my
design a bit.

Thanks,
Brandon
39100495c9937c39b2e0c704444e1b4a?d=identicon&s=25 Pat Maddox (Guest)
on 2009-04-19 10:27
(Received via mailing list)
Check out http://gist.github.com/97967 - top one is a custom example
group, and bottom one is accessing an example's instance var from
within a matcher.

Pat

On Sat, Apr 18, 2009 at 11:31 PM, Brandon Olivares
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-04-19 14:10
(Received via mailing list)
On Sun, Apr 19, 2009 at 5:10 AM, Pat Maddox <pat.maddox@gmail.com>
wrote:
> Check out http://gist.github.com/97967 - top one is a custom example
> group, and bottom one is accessing an example's instance var from
> within a matcher.

I forked this and modified it a bit: http://gist.github.com/98041

This shows how to set up class methods that you can access outside
examples (inside groups) and instance methods you can access inside
the examples.

Also - just a word of caution - even though you *can* access instance
variables in matchers, it seems very brittle to me to attach matchers
to specifically named instance variables like that.

Can you show us what you're trying to do? Perhaps there are less
brittle alternatives.

Cheers,
David
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (devbanana)
on 2009-04-19 14:26
(Received via mailing list)
>
Thank you very much! That helps a lot.

One more question: is it possible to specify that an example group must
be
nested within another example group? Say I want to act on controller, so
I'd
like it to be within a controller example group. Only thing I can think
of
is to check described_class, but are there any other methods? I'd like
to
check the class of the group it's nested in.

Thanks,
Brandon
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-04-19 14:33
(Received via mailing list)
On Sun, Apr 19, 2009 at 9:04 AM, Brandon Olivares
<programmer2188@gmail.com> wrote:
>> group, and bottom one is accessing an example's instance var from
>> within a matcher.
>>
>
> Thank you very much! That helps a lot.
>
> One more question: is it possible to specify that an example group must be
> nested within another example group? Say I want to act on controller, so I'd
> like it to be within a controller example group. Only thing I can think of
> is to check described_class, but are there any other methods? I'd like to
> check the class of the group it's nested in.

Why would you want this ensurance/protection? Are you planning on
writing controller examples in your coffee-maker example group? Seems
like overkill to me.

FWIW,
David
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (devbanana)
on 2009-04-19 14:43
(Received via mailing list)
> > group, and bottom one is accessing an example's instance var from
> > within a matcher.
>
> I forked this and modified it a bit: http://gist.github.com/98041
>
> This shows how to set up class methods that you can access outside
> examples (inside groups) and instance methods you can access inside
> the examples.
>

Thank you. That also helps a lot.

> Also - just a word of caution - even though you *can* access instance
> variables in matchers, it seems very brittle to me to attach matchers
> to specifically named instance variables like that.
>
> Can you show us what you're trying to do? Perhaps there are less
> brittle alternatives.
>

Well, as an example, let's say I would want to be able to do something
like
this, where the matcher is aware of its context:

describe '/posts', :type => :route do
  with_method :get do
    it { should be_accepted }
    it { should map_to_action 'index' }
  end
end

Well that with_method would either be a class method like that or use
context somehow.

But either way, in that example, I'd like be_accepted to know it's
testing
the /posts route, for method GET.

Thanks,
Brandon
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-04-19 15:03
(Received via mailing list)
On Sun, Apr 19, 2009 at 9:32 AM, Brandon Olivares
<programmer2188@gmail.com> wrote:
>> wrote:
>
> Well, as an example, let's say I would want to be able to do something like
> context somehow.
>
> But either way, in that example, I'd like be_accepted to know it's testing
> the /posts route, for method GET.

You should be able to get what you're after like this:

A class method:

class MyGroup < Spec::ExampleGroup
  class << self
    def with_method(verb)
      subject { send(verb, description_args.first) }
      yield
    end
  end
end

This is a bit tricky, but basically description_args is an array of
all the arguments sent to describe() (including in nested groups). The
first one, in your example above, is '/posts'. So the result of this
code is that when you call with_method(:get), the subject becomes the
result of calling get '/posts'.

Give that a shot and let us know if it works.

Also - you really don't need custom groups for this sort of thing,
because you can define with_method in a module and extend the
configuration w/ that module:

  module WithMethod
    def with_method(verb)
      subject { send(verb, description_args.first) }
      yield
    end
  end

  Spec::Runner.configure {|c| c.extend(WithMethod)}

Cheers,
David
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (devbanana)
on 2009-04-19 15:53
(Received via mailing list)
>
> all the arguments sent to describe() (including in nested groups). The
>   module WithMethod
>     def with_method(verb)
>       subject { send(verb, description_args.first) }
>       yield
>     end
>   end
>
>   Spec::Runner.configure {|c| c.extend(WithMethod)}
>

Thanks. I'm not sure I want to actually do a request though, because
it's
really only testing the routes in that example.

I know there are already ways of spec'ing the routes of course, but I'm
trying to learn more about customizing RSpec. And I really like this
syntax.

So as a more expanded version, let's say this:

describe '/posts' do
  with_method :get do
    it { should be_accepted }
    it { should map_to_action 'index' }
  end

  with_method :post do
    it { should be_accepted }
    it { should map_to_action 'create' }
  end

  with_method :put do
  it { should_not be_accepted }
  end

  with_method :delete do
  it { should_not be_accepted }
  end
end

Thanks,
Brandon
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-04-19 16:08
(Received via mailing list)
On Sun, Apr 19, 2009 at 8:50 AM, Brandon Olivares
<programmer2188@gmail.com> wrote:
>>
>>
>> configuration w/ that module:
>
>    it { should be_accepted }
>  end
>
>  with_method :delete do
>  it { should_not be_accepted }
>  end
> end

So you probably want to wrap asset_recognizes and assert_generates in
custom matchers at that point:

def with_method(verb) do |verb|
  subject { {:path => description_args.first, :method => verb } }
  yield
end

Spec::Matchers.define :be_accepted do
  match do |path_and_method|
    wrap_expectation do
      assert_recognizes path_and_method
    end
  end
end

That's all off the top of my head, so it might not work exactly as/is
- but that's the basic idea.

Cheers,
David
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-04-19 16:14
(Received via mailing list)
On 19 Apr 2009, at 13:58, David Chelimsky wrote:

>
>  Spec::Runner.configure {|c| c.extend(WithMethod)}
>
> Cheers,
> David

Or even (if I'm following this correctly) just define it right there
inside your describe block:

describe "custom example group" do

   # available in groups
   def self.custom_example_group_method
     puts "hello from a custom example group class"
   end

   # available in examples
   def custom_example_method
     puts "hello from a custom example group instance"
   end

   custom_example_group_method

   it "provides access to instance methods" do
     custom_example_method
   end
end

Obviously this doesn't give you any re-use yet, but it's usually the
very first step I take before factoring out a module like David's
suggested. I've yet to need to take it as far as the custom example
group class, but I like the concept.

Matt Wynne
http://blog.mattwynne.net
http://www.songkick.com
F85bacbbd4814799d4526b3e35a431df?d=identicon&s=25 Brandon Olivares (devbanana)
on 2009-04-19 16:48
(Received via mailing list)
> def with_method(verb) do |verb|
> end
>
> That's all off the top of my head, so it might not work exactly as/is
> - but that's the basic idea.
>

Oh, perfect. I didn't think of making the subject a hash. Is it best
just to
use it within any matcher using that DSL syntax?

Thanks,
Brandon

Just one more question. When should you use wrap_expectation?
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-04-19 19:10
(Received via mailing list)
On Sun, Apr 19, 2009 at 9:39 AM, Brandon Olivares
<programmer2188@gmail.com> wrote:
>> custom matchers at that point:
>>     end
> Thanks,
> Brandon
>
> Just one more question. When should you use wrap_expectation?

http://rspec.rubyforge.org/rspec/1.2.4/classes/Spe...
This topic is locked and can not be replied to.