Making the Switch

Our goal is to introduce a user interface for requesting the cash withdrawal. We want Cucumber to cover us as we make these changes, so we need to change how our test code interacts with the application. Up until now, all our step definitions were talking directly to the domain model. We’re going to change that so that some of them hit the new user interface instead (see Figure 8, Introducing the user interface). Which steps need to change?

images/support-add-ui.png

Figure 8. Introducing the user interface

Let’s take a look at our scenario again:

support_code/11/features/cash_withdrawal.feature
 
Feature:​ Cash Withdrawal​
 
Scenario:​ Successful withdrawal from an account in credit​
 
Given ​my account has been credited with $100​
 
When ​I withdraw $20​
 
Then ​$20 should be dispensed​
 
And ​the balance of my account should be $80

We haven’t specified anything about how we do the cash withdrawal, so there’s nothing we need to change about the scenario at all. Great! Let’s jump down into the step definition for withdrawing cash and see what needs to change there:

support_code/11/features/step_definitions/teller_steps.rb
 
When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ ​do​ |amount|
 
teller.withdraw_from(my_account, amount)
 
end

All we’re doing here is calling withdraw_from on something. Right now that something is an instance of the Teller class in our domain model. But what if we made teller return something else, like an object that represents the user interface? That’s the beauty of object-oriented programming—as long as the object understands the withdraw_from method, this step definition is going to be happy. Let’s leave this step definition alone and drop down into our World module:

support_code/11/features/support/world_extensions.rb
 
module​ KnowsTheDomain
 
def​ my_account
 
@my_account ||= Account.new
 
end
 
 
def​ cash_slot
 
@cash_slot ||= CashSlot.new
 
end
 
 
def​ teller
 
@teller ||= Teller.new(cash_slot)
 
end
 
end
 
 
World(KnowsTheDomain)

What we need to do is reimplement the teller method to return a class that knows how to automate the user interface. For now, just build an empty class with the right method on it, and change teller to return an instance of the new class:

support_code/12/features/support/world_extensions.rb
 
module​ KnowsTheUserInterface
 
class​ UserInterface
 
def​ withdraw_from(account, amount)
 
end
 
end
 
 
def​ my_account
 
@my_account ||= Account.new
 
end
 
 
def​ cash_slot
 
@cash_slot ||= CashSlot.new
 
end
 
 
def​ teller
 
@teller ||= UserInterface.new
 
end
 
end
 
World(KnowsTheUserInterface)

Now we’ve disconnected the action-invoking When step in our scenario from the domain model and connected it to this new UserInterface class. We’ve also renamed the module to reflect its new responsibility. If you run cucumber now, you should see the scenario fail:

 
Feature: Cash Withdrawal
 
 
Scenario: Successful withdrawal from an account in credit
 
Given my account has been credited with $100
 
When I withdraw $20
 
Then $20 should be dispensed
 
I’m empty! (RuntimeError)
 
./lib/nice_bank.rb:30
 
./features/step_definitions/cash_slot_steps.rb:2
 
features/cash_withdrawal.feature:5
 
And the balance of my account should be $80
 
 
Failing Scenarios:
 
cucumber features/cash_withdrawal.feature:2
 
 
1 scenario (1 failed)
 
4 steps (1 failed, 1 skipped, 2 passed)
 
0m0.002s
images/withdrawal-ui-wireframe.png

Figure 9. Wireframe for cash withdrawal user interface

The scenario has failed because our new support module hasn’t been wired up to the system yet, so nothing has been found in the cash slot. Now we have a goal: get that scenario to pass again, but this time, through the user interface.

Designing the User Interface

So, what should this shiny new user interface look like? We gathered around the whiteboard with our user experience team and sketched out a wireframe for the first iteration of the cash withdrawal form. The plan is for it to look roughly like Figure 9, Wireframe for cash withdrawal user interface.

Let’s flesh out our UserInterface class to talk to that form:

support_code/13/features/support/world_extensions.rb
 
class​ UserInterface
 
include Capybara::DSL
 
 
def​ withdraw_from(account, amount)
 
visit ​'/'
 
fill_in ​'Amount'​, :with => amount
 
click_button ​'Withdraw'
 
end
 
end

We’ve included Capybara’s DSL module in the UserInterface class to give us these new methods that let us carry out actions on the web user interface. First we visit the home page, then we fill_in the field labeled Amount, and finally we click the Withdraw button. This is the design we just looked at on the wireframe, formalized in code.

When we run cucumber against this, it’s going to fail of course, because we haven’t built the form yet. Let’s run it anyway just to check we’re on track:

 
Feature: Cash Withdrawal
 
 
Scenario: Successful withdrawal from an account in credit
 
Given my account has been credited with $100
 
When I withdraw $20
 
Unable to find field "Amount" (Capybara::ElementNotFound)
 
./features/support/world_extensions.rb:9
 
./features/step_definitions/teller_steps.rb:2
 
features/cash_withdrawal.feature:4
 
Then $20 should be dispensed
 
And the balance of my account should be $80
 
 
Failing Scenarios:
 
cucumber features/cash_withdrawal.feature:2
 
 
1 scenario (1 failed)
 
4 steps (1 failed, 2 skipped, 1 passed)
 
0m0.148s

OK, so Capybara is telling us that there isn’t an Amount field to fill in. Wouldn’t it be nice to be able to see what Cucumber is seeing? We could start Sinatra and run a manual test, but it would be more useful to see exactly what’s in the browser when the test fails. To do this, we need to take some time out to learn about a new feature of Cucumber.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.149.238