Acceptance Testing

Andy Lindeman

Levels of Testing

Roughly speaking...

  • Unit Tests
  • Integration Tests
  • Acceptance Tests

Unit Tests

  • Isolation
  • Dependencies often mocked or stubbed
  • No filesystem, network, database (Rails "unit" tests)
  • Programmer Tests

Acceptance Tests

A few quick examples...

Unit Test

            describe BankAccount do
              specify "new accounts have a zero balance" do
                account =
                expect(account.balance).to eq(BigDecimal("0.00"))

Acceptance Test


            As a depositor
            In order to safeguard my money
            I want to hold it in a bank account

            Given I am a depositor
            When I open a new bank account
            Then my balance is $0.00

Acceptance Test

            Given /I am a depositor/ do
              @user = Factory(:depositor)

            When /I open a new bank account/ do
              click_button "New Account"

            Then /my balance is $([0-9.]+)/ do |bal|
              expect(page).to have_text("Balance: $#{bal}")



  • Translate a user story into an acceptance test (or few)
  • Watch the acceptance test fail
  • Write a failing unit test
  • Watch the unit test fail
  • Make the unit test pass
  • Refactor
  • Watch the acceptance test get a bit farther
  • Repeat until all tests are passing
  • Confidently deliver the feature

Good Practices

In my very humble opinion ...

Seriously, I'm still learning this stuff too

But here's some things I've observed

Hide Implementation Details

            Given I am a newsletter publisher
            When I send out an electronic newsletter
            Then when Resque jobs are run
            And after 5 minutes passes
            Then the newsletter is received by all recipients

Hide Implementation Details

            Given I am a newsletter publisher
            When I send out an electronic newsletter
            Then all recipients receive my newsletter within 5 minutes
How would your customer talk to others about this feature? How would a salesperson?

Avoid "Programming"

            Given I am a user named "Alice"
            And there is a user "Bob" who is my friend
            And there is a user "Charlie" who is a friend of "Bob"
            When "Alice" invites "Bob" to a party
            But "Alice" does not invite "Charlie" to the same party
            Then "Alice" receives a message
              "Do you also want to invite Charlie?"
No customer or user would ever describe the feature like this ...

Avoid "Programming"

            As a party planner
            In order to have the best party
            I want to invite as many fun people as possible

            Given that I am planning a party
            When I invite my friend Alice
            Then I am asked if I might want to invite her friend Bob


            Given I do not yet have an account with
            When I register for an account
            And I correctly decipher the CAPTCHA image proving
              I am a human
            And I correctly input the code on my RSA SecurID dongle
            And I deposit $20.00
            Then my account balance is $20.00
What is this feature really about?


            Given I am a depositor
            When I deposit $20.00
            Then my account balance is increased by $20.00
Some other features should describe that complicated sign up process if it is important.
... but not every feature that implicitly requires signing up.

Use Consistent Terminology

            Given I am a user who has forgotten his password
            When I request that my password be reset
            Then I receive an email with instructions
              to replace my passcode
This example is obvious ("password" vs. "passcode") and ("reset" vs. "replace").
... but others are less so. Multiple terms for the same thing are confusing for everyone.
... or might be telling you that the feature is not fully fleshed out yet.

Don't Exhaustively Test Edge Cases

            Given I am a depositor
            And my account balance is $50.00
            When I attempt to withdraw $49.99
            Then the transaction is allowed
            And my balance is $0.01

            Given I am a depositor
            And my account balance is $50.00
            When I attempt to withdraw $50.01
            Then the transaction is not allowed
            And my balance remains $50.00
Test many edge cases if they are important, but not as acceptance tests.

But Why?

This is tedious and nobody but me will ever read these.

Your customers can write them

Highgroove -

Your customers can write them

  • They can do this. You may just need to clean up their prose a bit.
  • Now impress them: "the stories you write are being used to verify I implement features correctly"
  • "... and that these features do not break in the future"

My customer doesn't care

Are you sure?

Have you asked them?

Have they seen good acceptance tests in action?

Seriously, they don't care

Admittedly, that's sad

But what if writing them still had benefits?

Other Benefits

  • You as a developer are forced to think as a user
  • ... or a business person
  • "I need this database table and this model and this controller" vs. "what value is this feature providing?" is hard
  • Acceptance tests help formalize that transition

Other Benefits

  • One-stop shop for "what does this application do?" in language everyone can read
  • New developers
  • New project managers
  • Salespeople (oh, we have to know what this thing does to make money?

Other Benefits

  • Who has tried writing real isolated unit tests?
  • It was awesome, right?
  • But did you ever run into issues integrating those isolated objects, esp. after refactoring?
  • Did you have good higher-level tests?
  • ... ones that you trusted and were not brittle?

Tests Should Make You Go Fast

  • Good acceptance tests: have your cake and eat it too.
  • Fast unit tests that drive good design, facilitate refactoring and encourage cohesion.
  • Slower, end-to-end tests that verify everything fits together properly and provides value to customers.

By the way ...

You can write acceptance tests without Cucumber

RSpec and Capybara

            feature "Accounts" do
              include AccountSteps

              scenario "new accounts start with a zero balance" do

RSpec and Capybara

            module AccountSteps
              def login_as_depositor
                # click_on ...
                # fill_in ...

              def open_new_account
                # ...

              def balance_should_be(balance)
                expect(page).to have_text("Balance: $#{balance}")

Be wary of

            feature "Accounts" do
              scenario "transferring money between accounts" do
                visit root_path
                click_on "Login"
                fill_in "username", with: ""
                page.execute_script "$('omg_js').hack()"
                # ...

Be wary of

  • Acceptance tests themselves should not change unless the feature itself changed appreciably
  • The code behind may change slightly as the view layer changes

That's all folks