I agree with Sam -- having a higher-level, more abstract testing API
has its uses, but it has its costs, too, and you have to weigh the
costs vs the benefits.

Here's a technique I've used on several projects:

https://github.com/seomoz/interpol/blob/v0.8.1/spec/unit/interpol/configuration_spec.rb#L476-L497

(Notice also the following examples that use that method).

In this case, I've defined a class method that defines an example
group and defines some corresponding helper objects using `let`.  Then
I eval the provided block so at the callsite, examples can be defined.

I would never start here, but once I've found myself defining the same
sort of context 3 or more times, having a helper method which does
that for me makes a lot of sense.  Note that this doesn't really
change the of the specs much; it's still very obviously RSpec.  I also
left the definition of the context-defining method in-line so that
it's easy to see what it does.

HTH,
Myron

On Feb 7, 3:32 pm, Sam Goldman <[email protected]> wrote:
> I'm not sure if this is what you are looking for, but I do have some
> experience with writing DSLs on top of RSpec.
>
> I made a project that lets you write tests for HTTP APIs in a bit more of a
> straightforward manner[1]. This isn't quite the level of DSL-ness that it
> seems you are going for—i.e., the specs using this library still very much
> look like RSpec.
>
> In the context of this project, I think that it was worth it. I very much
> enjoy writing specs in the style afforded by this DSL.
>
> I think the tradeoff is about test noise. Let me define noise as details
> that are only relevant at a lower level of abstraction from the spec's
> level of abstraction. Ideally, your DSL will only hide noise. If your DSL
> hides signal, then consumers of your dialect will need to understand the
> internals.
>
> That said, I think it's a bad idea to start with a DSL. Write your tests
> directly, then once you have a good number of tests, ask yourself if a
> non-leaky abstraction exists would let you convey your specs at a higher
> level. You might find that this leads to a new domain concept for your
> production code. Alternatively it might be better to augment your tests
> with a little DSL.
>
> Sorry if that's a little too philosophical :P
>
> Sam
>
> 1.https://github.com/smartlogic/http_spec
>
> On Thu, Feb 7, 2013 at 8:26 AM, Ash Moran <[email protected]>wrote:
>
>
>
>
>
>
>
> > Hi
>
> > Someone on another mailing list I'm on recently posted asking for people's
> > thoughts on test naming practices, and writing my reply made me think about
> > some of the techniques I use to improve naming and remove duplication in my
> > own spec files.
>
> > The most worked-through example I have is the contract test for my
> > solutions to the Tennis Kata[1]. (I'm not implying this is the best way to
> > tackle the Tennis Kata.) Like with everything other spec suite, I started
> > out using plain describe/context/it type language[2], which contains a lot
> > of duplication:
>
> >   describe "scoring" do
> >     before(:each) { tennis.start_game }
>
> >     context "with no advantages" do
> >       context "A" do
> >         before(:each) { tennis.point_to_player_a }
> >         specify { expect(@score).to be == "15-0" }
>
> >         context "A" do
> >           before(:each) { tennis.point_to_player_a }
> >           specify { expect(@score).to be == "30-0" }
> >         end
>
> >         context "B" do
> >           before(:each) { tennis.point_to_player_b }
> >           specify { expect(@score).to be == "15-15" }
>
> > And it so goes on for quite a bit longer in the same style. The first step
> > is to factor out the duplication into context helpers, which leaves code
> > like this:
>
> >   game_started do
> >     score_is_now "0-0"
>
> >     context "with no advantages" do
> >       point_to_player :a do
> >         score_is_now "15-0"
>
> >         point_to_player :a do
> >           score_is_now "30-0"
> >         end
>
> > The problem now is that the meaning of the specification is now hidden in
> > the implementation of the helpers, in this case it was in spec_helper[3].
> > This gives very poorly composed methods with mixed levels of abstraction.
> > So the final step is to parameterise the spec DSL with blocks of code from
> > the specs itself, allowing you to write:
>
> >   specification_dsl :tennis do
> >     for_context :game_not_started do
> >       nothing
> >     end
>
> >     for_context :game_started do
> >       tennis.start_game
> >     end
>
> >     for_context :point_to_player do |player|
> >       # Heh, just noticed writing this email that I could be doing
> >       # tennis.send(:"point_to_player_#{player}") here, hey ho
> >       player == :a ? tennis.point_to_player_a : tennis.point_to_player_b
> >     end
>
> >     for_context :deuce do
> >       3.times do
> >         tennis.point_to_player_a
> >         tennis.point_to_player_b
> >       end
> >     end
>
> >     to_expect :score_is_now do |expected_score|
> >       expect(@scores.last).to be == expected_score
> >     end
>
> > This has finally put the spec and its definition back together[4], with
> > the DSL definition and its voodoo metaprogramming hidden away in
> > spec_helper[5].
>
> > Unfortunately there's a problem with this implementation, which is that it
> > fools RSpec into thinking expectation failures are all coming from
> > spec_helper.rb, which makes for rather useless error messages. I haven't
> > investigated this.
>
> > Anyway, the point of explaining this example is to ask for people's
> > opinions myself. A few obvious questions are:
>
> > * What sort of DSL-building have you tried/seen?
> > * Is this worth the effort over e.g. helper modules and custom matchers?
> > (E.g. is the terseness worth the indirection?)
> > * Is this possible in a simpler way with existing context tools in RSpec?
> > * If not, is it worth trying to make this DSL definition reusable?
> > * Are the situations where this is useful inherently best tackled another
> > way?
>
> > I'm interested in any opinions though, especially if you have a valid
> > reason why this is a bad idea / approach. I've sat on it long enough I'm
> > clearly not going to have any more insights on my own any time soon.
>
> > Cheers
> > Ash
>
> > [1]http://codingdojo.org/cgi-bin/wiki.pl?KataTennis
> > [2]
> >https://github.com/ashmoran/tennis_kata/blob/ff46b7e502337988940ef9ed...
> > [3]
> >https://github.com/ashmoran/tennis_kata/blob/a159bac3c3d6180c6f1b8377...
> > [4]
> >https://github.com/ashmoran/tennis_kata/blob/master/spec/tennis_contr...
> > [5]
> >https://github.com/ashmoran/tennis_kata/blob/master/spec/spec_helper.rb
>
> > --
> >http://www.patchspace.co.uk/
> >http://www.linkedin.com/in/ashmoran
>
> > _______________________________________________
> > rspec-users mailing list
> > [email protected]
> >http://rubyforge.org/mailman/listinfo/rspec-users
>
>
>
> _______________________________________________
> rspec-users mailing list
> [email protected]http://rubyforge.org/mailman/listinfo/rspec-users

-- 
You received this message because you are subscribed to the Google Groups 
"rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to