Custom Example Groups


#1

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


#2

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 O.


#3

On Sun, Apr 19, 2009 at 5:10 AM, Pat M. removed_email_address@domain.invalid
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


#4

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


#5

On Sun, Apr 19, 2009 at 9:04 AM, Brandon O.
removed_email_address@domain.invalid 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


#6

On Sun, Apr 19, 2009 at 9:32 AM, Brandon O.
removed_email_address@domain.invalid 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


#7

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


#8

On Sun, Apr 19, 2009 at 8:50 AM, Brandon O.
removed_email_address@domain.invalid 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


#9

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


#10

On 19 Apr 2009, at 13:58, David C. 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 W.
http://blog.mattwynne.net
http://www.songkick.com


#11

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?


#12

On Sun, Apr 19, 2009 at 9:39 AM, Brandon O.
removed_email_address@domain.invalid 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/Spec/Matchers.html#M000441