Took a little time today to give the site a makeover, and fix some small bugs. I still don't claim to have any eye for aesthetics, but I think this probably looks a lot nicer than before. Clean & simple.
I also released my first public Rails plugin this morning (with the wildly clever name of RestfulCrudIntegrationTester), a whole 36 hours sooner than I thought I would yesterday.
I've just finally started delving into Integration Testing (yah, I know, I'm a little late to the party.) I was writing tests for two different projects (one at home and one at work) and noticed even though they were very different projects, I was duplicating a lot of low level integration test code. So... I extracted it out into a plugin.
Now, on both projects, and any others I use the plugin in, I can just say things like...
new_session_as('jon','Exc3l1ent_p@s5w0rd!') do |jon|
jon.updates_article(1,{:article => {:subject => 'New Subject'}})
end
And then the Plugin will go try to login as that user (using the sessions_controller), and exercise both the edit and update actions in the articles_controller, as well as confirm that the edit form has the correct action, method, and input elements for a RESTful update.
Note, that the plugin is only for very RESTful controllers (if you didn't get that already), and it makes a few big assumptions on how your Rails app works. I tried to make sure the assumptions followed Rails conventions as much as possible.
Some of the initial code came from Jamis Buck's post on the subject from over a year ago, (yah, yah, late to the party) so thanks to Jamis for getting me started on the right path.
Strangly, as long as Integration Testing has been around, I didn't find any existing plugins to provide this kind of basic framework. So, I'm hoping I didn't waste time reinventing the wheel, and instead have found an unfilled need and provided a worthwhile contribution to the Rails community. Time will tell on that.
Here's the complete README file from the 0.1 version of plugin.
RestfulCrudIntegrationTester
============================
Provides a framework of common integration
tests for RESTful CRUD interactions.
How To Use
==========
class ArticleTest < ActionController::IntegrationTest
include RestfulCrudIntegrationTester
def test_some_article_functionality
new_session_as('jonnyg','testing123') do |jon|
params = {
:article => {
:title => 'Foo Bites Bar',
:content => 'Bar was bitten by Foo yesterday.'
}
}
jon.creates_story(params)
params = {:article => {:title => 'Bar Bitten Badly'}}
jon.updates_story(1,params)
jon.reads_story(1)
jon.deletes_story(1)
end
end
end
Assumptions
===========
The plugin makes certain assumptions about your application.
Some of these can be bypassed or overridden if your app does
not conform.
* you have appropriate map.resources lines in your routes.rb
for any controllers you intend to test with these methods.
* sessions_path helper method returns the path to a controller
that controls user logins. (Provided automatically by
Rails if you have a sessions_controller and routes.rb
includes "map.resources :sessions".)
@ Override by adding your own sessions_path method to
application_helper
* your sessions controller accepts parameters in the form of...
{:user => {:username => 'xxx', :password => 'yyy'}}
@ Override by providing your own new_session_as method
that generates an appropriate session for your app
with the user provided.
Provided Methods
================
-Create Sessions-
-----------------
new_session:
creates generic session (i.e. user not logged into your site).
#example
new_session do |guest|
#guest.does_things here
end
new_session_as:
creates session as user with provided username and password.
***assumes that you have a 'sessions_controller' and that it
takes params in the form of...
{:user => {:username => 'xxx', :password => 'yyy'}}
-Do CRUD-
---------
All CRUD tasks can be passed a block to do additional assertions
sess.reads_():
performs a GET to _path()
asserts a :success response
yields if you passed a block containing more assertions
sess.creates_():
performs a GET to new__path
asserts a :success response
asserts page contains a form with a _path action
and a 'post' method.
asserts form contains appropriately named input elements
for each of the you provided.
i.e. 'person[first_name]'
performs a POST to _path passing
yields if you passed a block containing more assertions
if you did NOT pass a block
asserts a :redirect response
follows redirect
asserts a :success response
sess.updates_(,)
performs a GET to edit__path()
asserts a :success response
asserts page contains a form with a _path action
and a 'post' method.
asserts form contains a hidden input field named '_method'
and a value of 'put'.
asserts form contains appropriately named input elements
for each of the you provided.
i.e. 'person[first_name]'
performs a PUT to _path passing
yields if you passed a block containing more assertions
if you did NOT pass a block
asserts a :redirect response
follows redirect
asserts a :success response
sess.deletes_():
performs a DELETE to _path()
yields if you passed a block containing more assertions
if you did NOT pass a block
asserts a :redirect response
follows redirect
asserts a :success response

