Rails integration tests without stories

I’m looking to drive the development of a rails app that does nothing
but serve a JSON API. All of the models are well tested elsewhere, so
I needn’t worry about that. My only immediate goal is to be able to
fire off requests to a path and check the returned JSON.

I’ve tried a number of methods for this today, without being
particularly enthused about any of them.

I started with user stories, but the conversion to english was
difficult for something largely developer-facing. “When I get
/widgets/:id” where :id is actually determined behind the scenes did
not read well, and a lot of my expectations were tough to express in
sentence form. I’ve had a lot of success with stories in the
application that is related to this API, but they don’t seem to fit
well here.

I tried out using standard rails test/unit integration tests, but that
would be a suite largely divorced from all of my other specs, and I
miss the :should syntax. Requiring spec/expectations and spec/matchers
alleviated some of that, but
test_names_end_up_being_unpleasantly_long_and_cumbersome. I haven’t
yet looked at the test/unit interop support, but maybe that would be
helpful here?

I tried to make an IntegrationExampleGroup akin to the other rails
example groups:

module Spec::Rails::Example
class IntegrationExampleGroup < ActionController::IntegrationTest
Spec::Example::ExampleGroupFactory.register(:integration, self)
end
end
describe ‘GET /widgets’, :type => :integration do
it “should …” do
get ‘/widgets’
[…]
end
end

This roughly seems to work for the integration tests (I’d still need
to add in transactions, maybe a few other things), but for reasons I
haven’t yet figured out means all my other specs try to run within an
integration session.

Finally, I could just test the serialization methods on the model (or
build a WidgetSerializer), and then use mocks in controller specs. But
I’m really looking for full-stack specs here, so I’d like to avoid
that.

Any suggestions? The path of least resistance is probably using the
test/unit tests, followed closely by just sucking it up and having
awkward user stories. My preferred solution would be the
IntegrationExampleGroup, and I may yet get that to work, but I thought
I’d ask here while I sit on it for a bit.

Thanks

Kyle

On Jun 7, 2008, at 3:10 PM, Kyle H. wrote:

/widgets/:id" where :id is actually determined behind the scenes did
not read well, and a lot of my expectations were tough to express in
sentence form. I’ve had a lot of success with stories in the
application that is related to this API, but they don’t seem to fit
well here.

Can you post an example or two? I’d like to see what’s not working for
you. No promises I can offer up anything better, but just curious.

end

This roughly seems to work for the integration tests (I’d still need
to add in transactions, maybe a few other things), but for reasons I
haven’t yet figured out means all my other specs try to run within an
integration session.

That definitely shouldn’t happen. What you have here is very close to
what

Finally, I could just test the serialization methods on the model (or
build a WidgetSerializer), and then use mocks in controller specs. But
I’m really looking for full-stack specs here, so I’d like to avoid
that.

Any suggestions? The path of least resistance is probably using the
test/unit tests, followed closely by just sucking it up and having
awkward user stories. My preferred solution would be the
IntegrationExampleGroup, and I may yet get that to work, but I thought
I’d ask here while I sit on it for a bit.

The IntegrationExampleGroup should work. Let’s try to figure out why
you’re getting bleed into the other groups.

Cheers,
David

On Sat, Jun 7, 2008 at 3:16 PM, David C. [email protected]
wrote:

I started with user stories, but the conversion to english was
difficult for something largely developer-facing. “When I get
/widgets/:id” where :id is actually determined behind the scenes did
not read well, and a lot of my expectations were tough to express in
sentence form. I’ve had a lot of success with stories in the
application that is related to this API, but they don’t seem to fit
well here.

Can you post an example or two? I’d like to see what’s not working for you.
No promises I can offer up anything better, but just curious.

I’ve since rm’d what I wrote, but imagine the case where I need to
create a widget, and then I’d like to spec what Widgets#show should
return to me:

Given a green widget named Panel
When I get the JSON representation of the widget
Then it should include its buttons
And it should not include the widget’s super secret name

There’s nothing inherently wrong with this – I actually rather like
it. Reasonably succinct, and a nice high-level overview. But notice
that it doesn’t really document the API, which is the goal of my
specs. No mention that the widget is at /widgets/1. If I were to
mention the URL, I’d have to use something like the /widgets/:id I
mentioned, where :id is a bare string in the step name, but filled in
with whatever id I happened to get when I created a widget in step #1.
Nor is it very clear about what “should include its buttons” means.
Again, that’s a good thing if I were documenting behavior of something
closer to an application, but it’s awkward here.

Cf. to:

widget = create_widget(:name => ‘Panel’, :color => ‘green’)
3.times { create_button(:widget => widget) }

obj = get_json(‘/widgets/’ + widget.id)
obj[‘buttons’].length.should == 3
obj.should_not have_key(‘super-secret-name’)

(I would actually split these up into separate examples, but you get the
idea)

Given well chosen example group/example names, it feels like this will
be more useful when another developer opens them up to learn how to
consume the API. And the specdocs have ended up being almost identical
to the stories for the purposes of at-a-glance understanding.

The IntegrationExampleGroup should work. Let’s try to figure out why you’re
getting bleed into the other groups.

The issue was that my other specs are for the models, which are shared
between two applications in a plugin. Thus, the specs are living in
plugins/mylib/spec/integration/models – integration here opposed to
the unit tests we have in spec/unit, as they lean on other plugins
like will_paginate. That directory then matches :integration and they
rightfully try to use the IntegrationExampleGroup. Just an unfortunate
overlap of terminology. For now I’ve just opted to rename the
:integration type key to :api, but hopefully I’ll come up with a
better name for spec/integration. Open to suggestions. =)

Works like a charm now, I can use autotest to run them alongside our
other specs, and it accomplishes everything I set out for. Big wins
all around.

k