How to write your first acceptance test

Rubyists always talk about testing. The growth of Ruby (and Rails) came at a time when automated testing and agile development also came to the fore. And much of the flexibility around agile depends upon being able to safely make changes to your development plan after the project has started.

But testing is not easy.

It can be frustrating.

It takes time. Sometimes a lot of time.

And those deadlines keep on coming.

You and I don’t have time to waste.

But it’s not an all-or-nothing proposition

You don’t need to test everything.

You don’t need to write tests first (but you do need to test that your tests are testing what you think they are testing).

However, what you do need are acceptance tests.

By default, Rails calls these “integration tests”. But you could call them user features, stories or full-stack tests. These are tests that defines how the next feature in your application is supposed to work. That run through the application from user-interface to database and proves that it does what it’s supposed to. At least in the most important paths through the code.

These tests are sometimes complex to write. They’re slower to run. They sometimes fail in weird ways.

But they prove that your application does it’s supposed to do.

They’re not a design tool. They’re not a methodology. They’re not some silver bullet.

They’re just a way to prove that, when you upgrade to the next version of Rails, or you switch to the latest version of Ruby, or one of your 3rd party gems fixes a security bug, that your application will keep on doing what it’s supposed to do.

So what does one of these tests look like?

Here’s a real one from one my projects…

It looks pretty simple, right?

This is “Gherkin”, the language behind Cucumber and my preferred testing tool, Spinach.

You can see how this document is actually the outcome of your discussions with the client – they say what they want, you write it out in Gherkin (asking questions as to what each clause actually means) and they approve it.

Then this needs converting into a “steps” file. In Spinach you run bundle exec spinach --generate and it builds an empty file for you. You then go through and fill it in with the actual actions a user would take.

Let’s take a closer look.

Firstly, we’re using Capybara to run the actual tests. This simulates a web-browser – in this case I’m using Rack::Test, but if you needed Javascript I use poltergeist (which in turn uses PhantomJS to run Webkit).

Then, I include a module called Logins. When you have steps that are shared across many stories, Spinach lets you define them in a module and just include them into your code. That deals with “Given I have an account” and “When I log in”.

“When I add a new report” is dealt with by clicking the “New Report” link, filling in some text fields on the form and then clicking the “Continue” button. Note – I’m using Rails’ internationalisation features to provide some separation from the text that actually appears on the page; clients love to tweak the wording of things and this way you only need to update the translation file – without it, you’d change the page, the test would fail and you’d have to make the same change to the test.

So you can see how each line in the original feature file maps to some Ruby code. And that Ruby code pretends to be a user, interacting with the application, reading the same text that the user sees, clicking the same links and buttons that the user would click.

When you run this (bundle exec spinach) it’s going to fail. Because you don’t have an application yet (of course, it’s fine to add in the test after you’ve written the code, as long as you make it fail first).

But now you know the exact bare minimum you need to write to get this story to pass – you need a link that says “New Report”, that brings up a form with two text-fields and a “Continue” button. And so on.

And once you’re done, you can run this spinach feature again and it will pass.

Then, in six months time, when you’ve added hundreds of other features, you can be sure that this original feature still works, no matter what changes you’ve made elsewhere in the application.

Even if you’ve upgraded your gems, moved to the newest version of Rails or changed your version of Ruby.

The test proves it works. For ever more.