[rspec-users] Building higher-level DSLs

Ash Moran ash.moran at patchspace.co.uk
Fri Feb 8 18:39:07 UTC 2013

On 7 Feb 2013, at 23:32, Sam Goldman <samwgoldman at gmail.com> 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.

This looks like a nice little DSL, and you're doing exactly what I was thinking of with the request contexts:

    describe "My Awesome App" do
      include HTTPSpec::DSL::Resource

      get "/foobar" do
        # … expectations …

I don't know how much work would be involved, but a few extra methods might make it cover HTTP completely, e.g. I could imagine

    it "should be successful" do
      status.should eq(200)

turning into

    response_has_status 200

or some such. This is the dilemma I run into, how long to spend adding things like this.

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

Very well put. I like the signal metaphor, and I will be stealing it, hope you don't mind :)

I suspect this fear of signal-hiding is the thinking process people are using when they don't use RSpec because it has "too much magic". (On a bigger scale, I suspect it's the reason some people use Python where Ruby is a viable option, as that seems to be a whole language founded on the concept of "no magic". I'm basing this on very limited knowledge though.) 

Leaky abstractions are a huge waste of time for consumers of so it puts a burden on the designer of a DSL to get it right. RSpec does this very well in my experience. Actually buggy software in general is a huge cause of waste, a leaky abstraction is just one type of bug.

> That said, I think it's a bad idea to start with a DSL.

I'm torn between agreeing with this and arguing for the opposite, which means there must be some rule I'm missing to decide between them.

When I use Cucumber, I write the DSL first and then figure out how to automate it. This has advantages in that the tests are independent of the interface(s) but does front-load some work writing an adapter layer. I've always got in the back of my mind that this is how Christopher Alexander said we should build buildings, create the language first and then use it to build a building, even if only one, and he seemed to have a good understanding of design. Mind you, he had several thousand years of architecture to extract patterns from first.

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

When I use RSpec I do this, and use duplication to locate missing domain concepts in the tests, just like you say. Maybe this is because it's possible to get quicker feedback by committing to an interface immediately. But that doesn't explain why I'd do that in RSpec and yet I wouldn't dream of using 'When I click on the "foo" div' steps in Cucumber. OTOH I *do* use Aruba, which directly contradicts that, but then I've found command line interfaces to be more stable, and they're also much simpler, and Aruba removes the front-loading effort. HTTPSpec might fall into this category.

> Alternatively it might be better to augment your tests with a little DSL.

Agreed that you can always improve later when you know for certain what domain concepts you need available first-class.

> Sorry if that's a little too philosophical :P

Heh, not at all, it's got me thinking (out loud, as usual) and is all practical ideas I can test.

I think what I'll do as an experiment is extract my RSpec meta-DSL, try to fix the error reporting line number bug, and do a few katas DSL-first (i.e. no plain `it` blocks and maybe no `context` blocks either - `describe` will probably stay). Then I'll be able to see what pain and/or benefits it has.



More information about the rspec-users mailing list