The Evolution of the Iridium Dialect
Read on to learn about the Iridium language, how it is most effectively written, and its benefits for end-to-end testing.
Join the DZone community and get the full member experience.
Join For FreeOne of the primary goals of Iridium is to allow testers, business analysts, and developers to write end-to-end tests in a language that is as close to English as possible. The Gherkin Given/Then/And/When syntax (upon which Iridium is built) is a great place to start, but in and of itself only guides the steps that you can write rather than providing a hard and fast dialect that testers should use.
The challenge with Iridium has always been to expose a set of steps that allow fluid and natural interaction with web applications via WebDriver, while also resulting in feature files that are easy to write and read.
The first collection of steps followed the approach that I have seen used in most other Cucumber/WebDriver implementations, with steps that allow you to identify elements using things like CSS selectors, ID attributes, and XPaths. An example of these steps would be:
And I click the element with the ID of "Continue"
Identifying elements in this manner makes sense to a developer, but it is not how you would describe the interaction with a web application to another person. To understand why, take a look at this example:
And I click the element with the xpath of "///*[@id="app-body"]/div[1]/d/div/div[4]/h5/button"
Not even the developer of the application could tell you what button that xpath refers to, which makes this step quite meaningless as a way of describing behavior.
To tackle this, Iridium introduces the notion of aliases. Aliases are nothing more than key/value pairs, and can be used to hide obtuse values, like the xpath above, behind a user friendly name. This means the step above can be rewritten like this:
Given the alias mappings
| Continue Button | //*[@id="app-body"]/div[1]/d/div/div[4]/h5/button |
...
And I click the element with the xpath alias of "Continue Button"
Referencing identifiers that mean nothing to human beings through aliases makes the step readable again.
But this solution can be improved on. While the reference to the button might be called “Continue Button,” the step still talks about xpaths, which again you would not do when describing the behaviour required to access a web application.
The second collection of steps introduced with Iridium moved away from these explicit notions of xpaths, CSS selectors and ID’s with what were called “found by” steps. These steps would attempt to match the element by testing the identifier, or identifier alias, as if it were an xpath, CSS selector, ID attribute, name attribute, value attribute, class, or normalized text content. There are some cases where these identifiers are not distinct (you could have a button with the ID and text content of submit for example), but in practice these situations were rare.
In this example, we would expect to click a button that has the text content of “Submit.”
And I click the element found by "Submit"
This new “found by” style of identifying elements was a big step in the right direction. Although the step above sounds a little robotic if you read it out aloud, it does come close to describing how a person would interact with a web application.
With the latest releases of Iridium there is a third style of step definitions that come very close to describing a test in the way that a person would describe it to another person. In the end, the change was quite a simple one: remove the phrase “element found by,” and add any description you like after the identifier. For example, the step above can now be written like this:
And I click the "Submit" button
Although it seems obvious in hindsight, it took a few revisions of Iridium to build a dialect that was both practical and natural. But with these steps in place, it is possible to write complete tests like the one shown below, which was taken from an actual working end-to-end test of an insurance web application.
Even without seeing the web application itself, it is not hard to understand what the test is doing, because the steps describe the actions being taken in a very natural format.
Feature: Home Price Test
Scenario: Setup Aliases
Given I set the alias mappings
| Postcode | postcode |
| Address Capture | address-typeahead |
| Address Suggestion | //*[@role="option"][1] |
| Rebuild Cost | //*[@ng-model="$ctrl.amount"] |
| Previous Insurance | //*[@ng-model="$ctrl.home.previousInsuranceCompanyCode"] |
| Continue | (.//button[.//*[contains(text(), 'Continue')]])[2] |
| Expiry Day | tiDay |
| Expiry Month | tiMonth |
| Expiry Year | tiYear |
| DOB Day | tiDay |
| DOB Month | tiMonth |
| DOB Year | tiYear |
| Mortgage | mortgageProvider |
| Final Price | .price-select-container__price |
Scenario: Generate some variables
Given I save the current date with the format "yyyy" to the alias "Year Moved In Input"
And I modify the alias "Year Moved In Input" by prepending it with "This Year ("
And I modify the alias "Year Moved In Input" by appending it with ")"
And I save the current date with the format "MMMM" to the alias "Month Moved In Input"
And I save the current date with the format "dd" to the alias "Policy Start Input"
And I modify the alias "Policy Start Input" by prepending it with "(//button[span[contains(., '"
And I modify the alias "Policy Start Input" by appending it with "')]])[not(@disabled)][last()]"
And I save the current date with the format "yyyy" to the alias "This Year"
And I save the current date with the format "MM" to the alias "This Month"
And I save the current date with the format "dd" to the alias "This Day"
And I dump the alias map to the console
Scenario: Open App
Given I open the page "https://homeinsurancewebapp.com"
And I set the default keystroke delay to "10" milliseconds
Scenario: Address Capture
Given I wait "10" seconds for the page to contain the text "What is the address of the property to be insured?"
Then I populate the "Postcode" textbox with "Postcode Input"
Then I populate the "Address Capture" textbox with "Address Input"
Then I click the "Address Suggestion" list item
Then I click the "Next" button
Scenario: Property Type
Given I wait "10" seconds for the page to contain the text "What type of property is the home?"
Then I click the "Building Type Input" button
Scenario: Body Corporate
Given I wait "10" seconds for the page to contain the text "Is the home part of a body corporate/strata title complex?"
Then I click the "Body Corporate Input" button
Scenario: Home built
Given I wait "10" seconds for the page to contain the text "What year was the home built?"
Then I click the "Year Built Input" button
Scenario: Wall Type
Given I wait "10" seconds for the page to contain the text "What is the main construction material for the walls?"
Then I click the "Wall Type Input" button
Scenario: Roof Type
Given I wait "10" seconds for the page to contain the text "What is the main construction material for the roof?"
Then I click the "Roof Type Input" button
Scenario: Home Occupied
Given I wait "10" seconds for the page to contain the text "How is the property occupied?"
Then I click the "Home Occupied Input" button
Scenario: Year Moved In
Given I wait "10" seconds for the page to contain the text "What year did you move in to the property?"
Then I click the "Year Moved In Input" button
Scenario: Month moved In
Given I wait "10" seconds for the page to contain the text "What month in 2017 did you move in to the property?"
Then I click the "Month Moved In Input" button
Scenario: Type of quote
Given I wait "10" seconds for the page to contain the text "What type of cover would you like a quote for?"
Then I click the "Quote Type Input" button
Scenario: Poicy Start Day
Given I wait "10" seconds for the page to contain the text "When would you like the policy to start?"
Then I click the "Policy Start Input" span
Then I click the "Next" button
Scenario: Rebuild Cost
Given I wait "10" seconds for the page to contain the text "What is the total cost to rebuild the home at today's prices?"
Then I populate the "Rebuild Cost" text box with "Rebuild Cost Input"
Then I click the "Next" button
Scenario: Business Activity
Given I wait "10" seconds for the page to contain the text "Is there any business activity conducted from the home?"
Then I click the "Business Activity Input" button
Scenario: Previous Insurance
Given I wait "10" seconds for the page to contain the text "Have you had home and/or contents insurance in the last 5 years?"
Then I click the "Had Previous Insurance Input" button
Scenario: Previous Insurance at Address
Given I wait "10" seconds for the page to contain the text "Was the previous insurance policy for this address"
Then I click the "Previous Insurance At Address Input" button
Scenario: Previous Insurance at Address
Given I wait "10" seconds for the page to contain the text "Who was your previous insurance with?"
Then I select "Previous Insurance Input" from the "Previous Insurance" drop down list
Then I click the "Next" button
Scenario: Previous Insurance Expiry
Given I wait "10" seconds for the page to contain the regex "What is the expiry date of your .+? policy\?"
Then I populate the "Expiry Day" text box with "This Day"
Then I populate the "Expiry Month" text box with "This Month"
Then I populate the "Expiry Year" text box with "This Year"
Then I click the "Next" button
Scenario: Previous Insurance Length
Given I wait "10" seconds for the page to contain the regex "How long have you had your .+? policy\?"
Then I click the "Previous Insurance Length Input" button
Scenario: Thefts, Burglaries or Claims
Given I wait "10" seconds for the page to contain the text "Thefts, Burglaries or Claims"
Then I click the "Thefts Input" button
Scenario: Date Of Birth
Given I wait "10" seconds for the page to contain the text "What is the policy holder's date of birth?"
Then I populate the "DOB Day" text box with "DOB Day Input"
Then I populate the "DOB Month" text box with "DOB Month Input"
Then I populate the "DOB Year" text box with "DOB Year Input"
Then I click the "Next" button
Scenario: Previous Insurance at Address
Given I wait "10" seconds for the page to contain the text "Will anyone older than the policy holder live in the home?"
Then I click the "Older Resident Input" button
Scenario: Email Address
Given I wait "10" seconds for the page to contain the text "Email Address"
Then I click the "Skip" button
Scenario: Terms Of Use
Given I wait "10" seconds for the page to contain the text "Terms of Use"
Then I click the "Yes, I Accept" button
Scenario: Jump to sales journey
Then I wait "10" seconds for the page to contain the regex "Your Smart (Home & Contents|Home Only|Contents Only) Insurance Quote"
And I wait "10" seconds for the "Continue" button to be clickable
And I click the "Continue" button
Scenario: Accept duty of disclosure
Then I wait "10" seconds for the page to contain the text "To finalise your insurance, we need a few more details"
And I click the "I Agree & Continue" button
Scenario: Bushfire, storm etc
Then I wait "10" seconds for the page to contain the text "Is the home under immediate threat of damage by severe storm, bushfire, grassfire or flood?"
And I click the "Bushfire Input" button
Scenario: Structurally sound
Then I wait "10" seconds for the page to contain the text "Is the home structurally sound and watertight?"
And I click the "Structurally Sound Input" button
Scenario: Neighbours
Then I wait "10" seconds for the page to contain the text "Do you have neighbours on all the borders of your property, excluding the street frontage?"
And I click the "Neighbours Input" button
Scenario: Neighbours
Then I wait "10" seconds for the page to contain the text "What borders the property?"
And I click the "Neighbour One" button
And I click the "Neighbour Two" button if it exists
Then I click the "Next" button
Scenario: Occupied during work hours
Then I wait "10" seconds for the page to contain the text "Is the home occupied during work hours?"
And I click the "Occupied Work Hours Input" button
Scenario: Currently Unoccupied
Then I wait "10" seconds for the page to contain the text "Is the home currently unoccupied?"
And I click the "Currently Unoccupied Input" button
Scenario: Mortgage
Then I wait "10" seconds for the page to contain the text "Is there a mortgage on the property?"
And I click the "Mortgage Input" button
Scenario: Mortgagee
Note that this screen will not be seen if there is no mortgage on the property
Then I wait "10" seconds for the page to contain the text "Is there a mortgage on the property?" ignoring timeouts
Then I select "Mortgagee Input" from the "Mortgage" drop down list if it exists
Then I click the "Next" button if it exists
Scenario: Undergoing construction
Then I wait "10" seconds for the page to contain the text "Is the home under construction or undergoing renovation, alteration or extension?"
And I click the "Undergoing Construction Input" button
Scenario: Undergoing construction
Then I wait "10" seconds for the page to contain the text "Refused or Cancelled Insurance"
And I click the "Refused Insurance Input" button
Scenario: Criminal Offence
Then I wait "10" seconds for the page to contain the text "Have you or any other household member been convicted of a criminal offence?"
And I click the "Criminal Offence Input" button
Scenario: Initial Price
Then I wait "10" seconds for the page to contain the text "Your Price"
And I sleep for "10" seconds
And I click the "Refine your cover" button
Scenario: Motor Burnout
Then I wait "10" seconds for the page to contain the text "Would you like to add Motor Burnout Cover?"
And I click the "Motor Burnout Input" button
Scenario: Accidental Damage
Then I wait "10" seconds for the page to contain the text "Would you like to add Accidental Damage Cover?"
And I click the "Accidental Damage Input" button
Scenario: Flood Cover
Then I wait "10" seconds for the page to contain the text "Would you like to add Flood Cover?"
And I click the "Flood Cover Input" button
Scenario: Final price
Then I wait "10" seconds for the page to contain the text "Your Price"
And I sleep for "10" seconds
And I save the text content of the "Final Price" h2 to the alias "Final Price Value"
And I modify the alias "Final Price Value" by replacing all characters that match the regex "^\$" with ""
And I modify the alias "Final Price Value" by dividing "100" into it
And I dump the value of the alias "Final Price Value" to the console
And I verify that the alias "Final Price Value" is larger than the alias "Final Price Lower Bound"
And I verify that the alias "Final Price Value" is smaller than the alias "Final Price Upper Bound"
If you are interested in writing end-to-end tests like the one shown above, check out the Iridium documentation page, and take a look at the courses available on Udemy. Iridium itself is a free and open source project on GitHub.
Opinions expressed by DZone contributors are their own.
Comments