Академический Документы
Профессиональный Документы
Культура Документы
Cucumber Techniques
Recipe 1
Ingredients
Ast::Table,1 Cucumbers table-crunching workhorse
Rubys built-in BigDecimal for representing currencies2
Solution
In this recipe, well assume were getting data from our app using a GUI
automation library or web scraping framework. The data will be in whatever
format the behind-the-scenes API provides. This format may be grisly, so we
dont want it in our human-readable Cucumber tests.
How do we address this mismatch between our top-level tests and the
underlying API? Well use Cucumber to transform the table in our .feature file
to whatever the API needs. We can change columns, convert data inside cells,
or perform tricky custom transformations.
This recipe comes in several flavors so that you can practice applying all these
techniques.
Renaming Headers
Imagine you have the following test steps:
tables/tables.feature
Scenario: Renaming headers
Given I am logged in as a buyer
When I search for available cars
Then I should see the following cars:
| color | model
|
| rust | Camaro |
| blue | Gremlin |
1.
2.
http://rdoc.info/github/cucumber/cucumber/Cucumber/Ast/Table
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/bigdecimal/rdoc/index.html
www.it-ebooks.info
Your team has standardized on the U.S. spelling of color, but the API youre
calling to scrape the data from your app happens to use the U.K. spelling.
tables/step_definitions/table_steps.rb
When /^I search for available cars$/ do
@cars = [{'colour' => 'rust', 'model' => 'Camaro'},
{'colour' => 'blue', 'model' => 'Gremlin'}]
end
If you compare these tables directly in Cucumber, youll get a test failure,
because the color column name in your examples doesnt match the colour key
returned by the API.
Cucumbers map_headers!() method lets you transform the table in your examples
into the format expected by your underlying API.
tables/step_definitions/table_steps.rb
Then /^I should see the following cars:$/ do |table|
table.map_headers! 'color' => 'colour'
table.diff! @cars
end
If your team members have written several scenarios and have been alternating
between spellingswell, you really should pick one and standardize. But in
the meantime, you can pass a regular expression or a block to map_headers!()
for more control over the column renaming.
table.map_headers! /colou?r/ => 'colour'
table.map_headers! { |name| name.sub('color', 'colour') }
What if you need to change the values inside the table, not just the headers?
Cucumber reads every table cell as a string. So, it will see the price of the
platinum plan, for instance, as the string '$1000'.
www.it-ebooks.info
One of our older projects used the RSpec Story Runner, Cucumbers predecessor. At
the time, the Story Runner didnt support tables or tags. For one particularly repetitive
test, we implemented our own ad hoc version.
# Modes: Regular, Analysis, Time
Scenario: Rounding
When I enter 1.000001
Then the value should be 1
We would preprocess the scenario in Ruby and generate three scenarios that would
put the hardware into Regular, Analysis, or Time mode before running the test.
Thank goodness Cucumber came along!
But this hypothetical used-car API returns the prices as BigDecimal values like
1000.0. It also furnishes some extra information youre not using for this test:
an administrative code for each plan.
tables/step_definitions/table_steps.rb
require 'bigdecimal'
When /^I view warranty options$/ do
_1000 = BigDecimal.new '1000'
_500 = BigDecimal.new '500'
_200 = BigDecimal.new '200'
@warranties = [{'name' => 'Platinum', 'price' => _1000, 'code' => 'P'},
{'name' => 'Gold',
'price' => _500, 'code' => 'G'},
{'name' => 'Silver',
'price' => _200, 'code' => 'S'}]
end
You need to convert the strings from your scenario into numbers to compare
against your API. You can do this with Cucumbers map_column!() method. It
takes a column name and a Ruby block to run on every cell in that column.
tables/step_definitions/table_steps.rb
Then /^I should see the following options:$/ do |table|
table.map_column!(:price) { |cell| BigDecimal.new(cell.sub('$', '')) }
table.diff! @warranties
end
Notice that Cucumber didnt complain that the API had an extra code column
thats not used in the scenario. In the next section, well talk about these
kinds of table structure differences.
www.it-ebooks.info
If you need the header row as well, you can call raw().
raw.rb(main):001:0> table.raw
=> [["color", "model"], ["rust", "Camaro"], ["blue", "Gremlin"]]
raw.rb(main):002:0>
If your headers are in the first column (rather than the first row), you can
transpose() the table or call rows_hash().
3.
Cucumber also allows you to ignore surplus or missing rows, but that use is rarer.
www.it-ebooks.info
transpose.rb(main):001:0> table.transpose
=>
|
color |
rust
|
blue
|
|
model |
Camaro |
Gremlin |
transpose.rb(main):002:0> table.rows_hash
=> {"color"=>"model", "rust"=>"Camaro", "blue"=>"Gremlin"}
transpose.rb(main):003:0>
Using the techniques in this recipe, you can keep your Cucumber features
in the language of the problem domain. The mundane details of data formats
and APIs will be confined to your Ruby step definitions, where they belong.
www.it-ebooks.info