It’s perfectly possible to write automated acceptance tests without using Cucumber. You can just write them in pure Ruby. Take this test for withdrawing cash from an ATM:
Scenario: Attempt withdrawal using stolen card Given I have $100 in my account But my card is invalid When I request $50 Then my card should not be returned And I should be told to contact the bank
We could automate that test using good old Test::Unit, perhaps something like this:
require 'test/unit' class WithdrawlTests < Test::Unit::TestCase def test_attempt_widthrawl_using_stolen_card bank = Bank.new account = Account.new(bank) account.deposit(100) card = DebitCard.new(account) bank.lock_out(card) atm = Atm.new(bank) atm.insert_card(card) atm.enter_pin(card.pin) atm.withdraw(50) assert atm.card_withheld?, "Expected the card to be withheld by the ATM" assert_equal "Please contact the bank.", atm.message_on_screen end end
The big disadvantage of writing acceptance tests in pure Ruby like this is that it’s unlikely you’ll be able to show this test to your team’s analyst without their eyes glazing over.
Unless your analyst is, or has recently been, a programmer themselves, they won’t be able to see past the noise of Ruby’s syntax, clean as it may be, to understand the actual behaviour that’s being specified. The specification of behaviour and the implementation of the test are all mixed up together, and that’s a problem if we want to get feedback from our stakeholders about whether we’ve specified the right thing before we go ahead and build it.
If we want the benefits of using plain language to write our behaviour specification, then we need a way to translate that into automation code that actually pulls and pokes at our application. Step definitions give you a translation layer between the plain-language specification of behviour and the test automation code, mapping the Gherkin steps of each scenario to Ruby code that Cucumber can execute.
The cost of this extra layer is complexity: Yes, you have more test code to maintain than you would if you stuck to writing your tests in pure Ruby. The benefit is clarity: by separating the what (the features) from the how (the ruby automation code), you keep each part simpler and easier for its target audience to understand.