<<

GKTCS Innovations Pvt. Ltd.

Cucumber,

Surendra Panpaliya

Director, GKTCS Innovations Pvt. Ltd, Pune.

18+ Years of Experience ( MCA, PGDCS, BSc. [Electronics] , CCNA) Director , GKTCS Innovations Pvt. Ltd. Pune [ Nov 2009 – Till date ] IT Manager and Consultant at Rolta India Ltd, Mumbai [ April 2007 – Oct 2009 ] Associate Professor at Sinhgad Institute , Pune [Jan 2002 – April 2007] Instructor , ITM Bangalore [ May 2000 – Jan 2002 ] Project Trainee, DSYS Group, Bangalore [ Jan 1999 to April 2000] Skills ❑ Ruby, Rail,Appium, Python, Jython, Django, Android , PHP, LAMP ❑ , Rspec, Watir, , ❑ UNIX /Linux Shell Scripting, System Programming ❑ Data Communication & Networking, CCNA

Author of 4 Books National Paper Presentation Awards at BARC Mumbai 2 Agenda ( Day 2)

• Introduction to Cucumber • Installation cucumber • cucumber commands • cucumber help • cucumber languages • Given, When, Then, And, Scenario, Background • Gherikin Introduction • Gherkin Syntax • BDD • Calculator Example • cash withdrawal Case Study using Cucumber. • Watir Framework

3 Introduction

What is Cucumber? • Cucumber is a software tool that computer use for testing other software. It runs automated acceptance tests written in a behavior-driven development (BDD) style. • Cucumber is written in the Ruby . • Cucumber projects are available for other platforms beyond Ruby.

4 Introduction

What is Cucumber? • Cucumber was originally a Ruby tool. • It grew out of the Ruby unit- level BDD framework . • The Ruby version of Cucumber is still the reference implementation and the most widely used, but there are now versions for Java, .NET, JavaScript, as well as several o t h e r l a n g u a g e s a n d platforms.

5 BDD

6 cucumber at a glance

7 cucumber at a glance

8 cucumber Integration

9 Introduction

2. Write a step definition in Ruby 1. Describe behaviour in plain text

3. Run and watch it fail 4. Write code to make the step pass

10 Introduction

5. Run again and see the step pass 6. Repeat 2-5 until green like a cuke

7. Repeat 1-6 until the money runs out Cucumber works with Ruby, Java, .NET, Flex or web applications written in any language. It Cucumber lets software development teams has been translated to over 40 spoken describe how software should behave in plain languages. text. The text is written in a business- readable domain-specific language and serves as documentation, automated tests and development-aid - all rolled into one format. 11 Behavior-driven development

It is a software development process that emerged from test-driven development (TDD). It combines the general techniques and principles of TDD with ideas from domain-driven design and object- oriented analysis and design to provide software development and management teams with shared tools and a shared process to collaborate on software development. BDD is principally an idea about how software development should be managed by both business interests and technical insight, the practice of BDD does assume the use of specialised software tools to support the development process. The tools serve to add automation to the ubiquitous language that is a central theme of BDD.

12 Behavior-driven development

BDD is largely facilitated through the use of a simple domain specific language (DSL) using natural language constructs (e.g., English-like sentences) that can express the behavior and the expected outcomes. Test scripts have long been a popular application of DSLs with varying degrees of sophistication. DSLs convert structured natural language statements into executable tests. The result is a closer relationship to acceptance criteria for a given function and the tests used to validate that functionality. As such it is a natural extension of TDD testing in general. BDD focused on: Where to start in the process What to test and what not to test How much to test in one go What to call the tests How to understand why a test fails

13 Behavior-driven development

At the heart of BDD is a rethinking of the approach to the unit testing and that naturally arise with these issues. For example, BDD suggests that unit test names be whole sentences starting with a conditional verb ("should" in English for example) and should be written in order of business value. Acceptance tests should be written using the standard agile framework of a User story: "As a [role] I want [feature] so that [benefit]". Acceptance criteria should be written in terms of scenarios and implemented as classes: Given [initial context], when [event occurs], then [ensure some outcomes] .

14 Behavior-driven development

15 The TDD Cycle

16 Installation

• Cucumber is installed with Ruby's package manager, RubyGems. • Assuming you already have a current version of Ruby to install Cucumber simply open a command window and run.

$gem install cucumber

• This will install Cucumber along with its dependencies. • If you are using Bundler, just add it to your Gemfile:

• group :test do • gem 'cucumber' • end

17 What is Bundler?

• Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed. • Bundler is an exit from dependency hell, and ensures that the gems you need are present in development, staging, and production. Starting work on a project is as simple as bundle install. • $ gem install bundler • $ bundle install • $ git add Gemfile Gemfile.lock

18 How Cucumber Works?

• Cucumber is a command-line tool. When you run it, it reads in your specifications from plain-language text files called features, examines them for scenarios to test, and runs the scenarios against your system. • Each scenario is a list of steps for Cucumber to work through. So that Cucumber can understand these feature files, they must follow some basic syntax rules. • The name for this set of rules is Gherkin. • Along with the features, you give Cucumber a set of step definitions, which map the business-readable language of each step into Ruby code to carry out whatever action is being described by the step. $cucumber • No such file or directory - features. You can use `cucumber --init` to get started.

19 How Cucumber Works?

20 How Cucumber Works?

• Surendras-MacBook-Pro:cucumber_ex SurendraMac$ cucumber --init create features create features/step_definitions create features/support create features/support/env.rb Surendras-MacBook-Pro:cucumber_ex SurendraMac$cucumber 0 scenarios 0 steps 0m0.000s Surendras-MacBook-Pro:cucumber_ex SurendraMac$ cucumber --version 2.1.0 Surendras-MacBook-Pro:cucumber_ex SurendraMac$ cucumber --help

21 Creating a Project

• SurendraMac$ mkdir calculator • $cucumber calculator • No such file or directory - calculator. You can use `cucumber --init` to get started. • cucumber --init • create features • create features/step_definitions • create features/support • create features/support/env.rb • Surendras-MacBook-Pro:calculator SurendraMac$

22 Creating a Project

SurendraMac$ ls features SurendraMac$ cd features/ :features SurendraMac$ ls -l total 0 drwxr-xr-x 2 SurendraMac staff 68 Nov 28 09:46 step_definitions drwxr-xr-x 3 SurendraMac staff 102 Nov 28 09:46 support

23 Creating a Project

#create file adding.feature Feature: Adding

Scenario Outline: Add two numbers Given the input "" When the calculator is run Then the output should be "" Examples: | input | output | | 2+2 | 4 | | 98+1 | 99 |

24 step_definitions

$cd step_definitions/ #calculator_steps.rb Given /^the input "([^"]*)"$/ do |input| @input = input end

When /^the calculator is run$/ do @output = `ruby calc.rb #{@input}` raise('Command failed!') unless $?.success? end

Then /^the output should be "([^"]*)"$/ do |expected_output| @output.should == expected_output end

Surendras-MacBook-Pro:step_definitions SurendraMac$

25 calc.rb

5) Create another ruby file in calculator folder >> “calc.rb” #calc.rb print eval(ARGV[0])

• First we read the command-line argument ARGV[0] and send it to Ruby’s eval method. That should be able to work out what to do with the calculation input. Finally, we print the result of eval to the console. • Finally we have completed our first program to run the program go to the terminal • Type cucumber it will display $cucumber and hit enter.

26 Test your First Project Now

$cucumber Feature: Adding

Scenario Outline: Add two numbers # features/adding.feature:3 Given the input "" # features/adding.feature:4 When the calculator is run # features/adding.feature:5 Then the output should be "" # features/adding.feature:6

Examples: | input | output | |2+2 | 4 | | 98+1 | 99 |

2 scenarios (2 passed) 6 steps (6 passed) 0m0.097s

27 Gherkin Basics

Gherkin is the language that Cucumber understands. It is a Business Readable, Domain Specific Language that lets you describe software’s behaviour without detailing how that behaviour is implemented. Gherkin serves two purposes — documentation and automated tests. The third is a bonus feature — when it yells in red it’s talking to you, telling you what code you should write. Gherkin’s grammar is defined in the Treetop grammar that is part of the Cucumber codebase. The grammar exists in different flavours for many spoken languages (37 at the time of writing), so that your team can use the keywords in your own language. There are a few conventions. Single Gherkin source file contains a description of a single feature. Source files have .feature extension.

28 Gherkin Basics

Gherkin, the language we use for writing Cucumber features. To write specifications for your software that can be both read by your stakeholders and tested by Cucumber. Learn what each of the Gherkin keywords does and how they all fit together to make readable, executable Cucumber specifications.

“The hardest single part of building a software system is deciding precisely what to build.” Better communication between developers and stakeholders is essential to help avoid this kind of wasted time. One technique that really helps facilitate

29 Gherkin Syntax

Gherkin is a line-oriented language that uses indentation to define structure. Line endings terminate statements (eg, steps). Either spaces or tabs may be used for indentation (but spaces are more portable). Most lines start with a keyword.

Comment lines are allowed anywhere in the file. They begin with zero or more spaces, followed by a hash sign (#) and some amount of text.

The parser divides the input into features, scenarios and steps. When you run the feature the trailing portion (after the keyword) of each step is matched to a Ruby code block called Step Definitions.

30 Gherkin Syntax

A Gherkin source file usually looks like this 1: Feature: Some terse yet descriptive text of what is desired 2: Textual description of the business value of this feature 3: Business rules that govern the scope of the feature 4: Any additional information that will make the feature easier to understand 5:

First line starts the feature. Lines 2–4 are unparsed text, which is expected to describe the business value of this feature.

31 Gherkin Syntax

6: Scenario: Some determinable business situation 7: Given some precondition 8: And some other precondition 9: When some action by the actor 10: And some other action 11: And yet another action 12: Then some testable outcome is achieved 13: And something else we can check happens too Line 6 starts a scenario. Lines 7–13 are the steps for the scenario.

32 Gherkin Syntax

14: 15: Scenario: A different situation 16: ...

Line 15 starts next scenario and so on.

33 Feature Introduction

Every .feature file conventionally consists of a single feature. A line starting with the keyword Feature followed by free indented text starts a feature.

A feature usually contains a list of scenarios. You can write whatever you want up until the first scenario, which starts with the word Scenario (or localized equivalent; Gherkin is localized for dozens of languages) on a new line. You can use tagging to group features and scenarios together independent of your file and directory structure.

34 Feature Introduction

Every scenario consists of a list of steps, which must start with one of the keywords Given, When, Then, But or And. Cucumber treats them all the same, but you shouldn’t. Here is an example:

Feature: Serve coffee Coffee should not be served until paid for Coffee should not be served until the button has been pressed If there is no coffee left then money should be refunded

35 Feature Introduction

Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ When I press the coffee button Then I should be served a coffee

36 Feature Introduction

In addition to a scenario, a feature may contain a background, scenario outline and examples. Respective keywords (in English) and places to read more about them are listed below. You can get a list of localized keywords with $cucumber --i18n help $cucumber -- i18n en keyword localized more info, see name ‘English’ native ‘English’ encoding ‘UTF-8’

37 Feature Introduction keyword localized more info, see feature ‘Feature’ Feature Introduction background ‘Background’ Background scenario ‘Scenario’ Feature Introduction scenario_outline ‘Scenario Outline’ Scenario outlines examples ‘Examples’ / ‘Scenarios’ Scenario outlines given ‘Given’ Given-When-Then when ‘When’ Given-When-Then then ‘Then’ Given-When-Then and ‘And’ Given-When-Then but ‘But’ Given-When-Then

38 Given When Then

Cucumber scenarios consist of steps, also known as Givens, Whens and Thens. Cucumber doesn’t technically distinguish between these three kind of steps. However, we strongly recommend that you do! These words have been carefully selected for their purpose, and you should know what the purpose is to get into the BDD mindset.

39 Given

The purpose of givens is to put the system in a known state before the user (or external system) starts interacting with the system (in the When steps). Avoid talking about user interaction in givens. If you were creating use cases, givens would be your preconditions. Examples: Create records (model instances) / set up the database state. It’s ok to call into the layer “inside” the UI layer here (in Rails: talk to the models). Log in a user (An exception to the no-interaction recommendation. Things that “happened earlier” are ok). And for all the Rails users out there – we recommend using a Given with a multiline table argument to set up records instead of fixtures. This way you can read the scenario and make sense out of it without having to look elsewhere (at the fixtures).

40 When

The purpose of When steps is to describe the key action the user performs (or, using Robert C. Martin’s metaphor, the state transition). Examples: Interact with a web page (Webrat/Watir/Selenium interaction etc should mostly go into When steps). Interact with some other user interface element. Developing a library? Kicking off some kind of action that has an observable effect somewhere else.

41 Then

The purpose of Then steps is to observe outcomes. The observations should be related to the business value/benefit in your feature description. The observations should also be on some kind of output – that is something that comes out of the system (report, user interface, message) and not something that is deeply buried inside it (that has no business value). Examples: Verify that something related to the Given+When is (or is not) in the output Check that some external system has received the expected message (was an email with specific content sent?) While it might be tempting to implement Then steps to just look in the database – resist the temptation. You should only verify outcome that is observable for the user (or external system) and databases usually are not.

42 And, But

If you have several givens, whens or thens you can write Scenario: Multiple Givens Given one thing Given another thing Given yet another thing When I open my eyes Then I see something Then I don't see something else

43 And, But

Or you can make it read more fluently by writing Scenario: Multiple Givens Given one thing And another thing And yet another thing When I open my eyes Then I see something But I don't see something else To Cucumber steps beginning with And or But are exactly the same kind of steps as all the others.

44 Background

Background allows you to add some context to the scenarios in a single feature. A Background is much like a scenario containing a number of steps. The difference is when it is run. The background is run before each of your scenarios but after any of your Before Hooks. Example: Feature: Multiple site support As a Mephisto site owner I want to host blogs for different people In order to make gigantic piles of money

45 Background

Background: Given a global administrator named "Greg" And a blog named "Greg's anti-tax rants" And a customer named "Dr. Bill" And a blog named "Expensive Therapy" owned by "Dr. Bill” Scenario: Dr. Bill posts to his own blog Given I am logged in as Dr. Bill When I try to post to "Expensive Therapy" Then I should see "Your article was published."

46 Background

Scenario: Dr. Bill tries to post to somebody else's blog, and fails Given I am logged in as Dr. Bill When I try to post to "Greg's anti-tax rants" Then I should see "Hey! That's not your blog!"

Scenario: Greg posts to a client's blog Given I am logged in as Greg When I try to post to "Expensive Therapy" Then I should see "Your article was published."

47 Good practices for using Background

Don’t use “Background” to set up complicated state unless that state is actually something the client needs to know. For example, if the user and site names don’t matter to the client, you should use a high-level step such as “Given that I am logged in as a site owner”.

Keep your “Background” section short. You’re expecting the user to actually remember this stuff when reading your scenarios. If the background is more than 4 lines long, can you move some of the irrelevant details into high-level steps? See Calling Steps from Step Definitions.

48 Good practices for using Background

Make your “Background” section vivid. You should use colorful names and try to tell a story, because the human brain can keep track of stories much better than it can keep track of names like “User A”, “User B”, “Site 1”, and so on.

Keep your scenarios short, and don’t have too many. If the background section has scrolled off the screen, you should think about using higher-level steps, or splitting the *.features file in two.

49 Step definitions

For each step Cucumber will look for a matching step definition. A step definition is written in Ruby. Each step definition consists of a keyword, a string or , and a block. Example:

# features/step_definitions/coffee_steps.rb

Then "I should be served coffee" do @machine.dispensed_drink.should == "coffee" end

50 Step definitions

Step definitions can also take parameters if you use regular expressions: # features/step_definitions/coffee_steps.rb Given /there are (\d+) coffees left in the machine/ do |n| @machine = Machine.new(n.to_i) end This step definition uses a regular expression with one match group – (\d+). (It matches any sequence of digits). Therefore, it matches the first line of the scenario. The value of each matched group gets yielded to the block as a string. You must take care to have the same number of regular expression groups and block arguments. Since block arguments are always strings, you have to do any type conversions inside the block, or use Step Argument Transforms.

51 Step definitions

When Cucumber prints the results of the running features it will underline all step arguments so that it’s easier to see what part of a step was actually recognised as an argument. It will also print the path and line of the matching step definition. This makes it easy to go from a feature file to any step definition. Take a look at Step Definitions and the examples directory to see more. Step definitions are defined in ruby files under features/step_definitions/ *_steps.rb. Here is a simple example: Given /^I have (\d+) cucumbers in my belly$/ do |cukes| # Some Ruby code here end

52 Step definitions

A step definition is analogous to a method definition / function definition in any kind of OO/procedural programming language. Step definitions can take 0 or more arguments, identified by groups in the Regexp (and an equal number of arguments to the Proc). It’s also possible to define Step Definitions using strings and $variables like this: Given "I have $n cucumbers in my belly" do |cukes| # Some Ruby code here end In this case the String gets compiled to a Regular Expression behind the scenes: /^I have (.*) cucumbers in my belly$/.

53 Step definitions

Then there are Steps. Steps are declared in your features/*.feature files. Here is an example:

Given I have 93 cucumbers in my belly A step is analogous to a method or function invocation. In this example, you’re “calling” the step definition above with one argument — the string “93”. Cucumber matches the Step against the Step Definition’s Regexp and takes all of the captures from that match and passes them to the Proc.

Step Definitions start with a preposition or an adverb (Given, When, Then, And, But), and can be expressed in any of Cucumber’s supported Spoken languages. All Step definitions are loaded (and defined) before Cucumber starts to execute the plain text.

54 Step definitions

When Cucumber executes the plain text, it will for each step look for a registered Step Definition with a matching Regexp. If it finds one it will execute its Proc, passing all groups from the Regexp match as arguments to the Proc.

The preposition/adverb has no significance when Cucumber is registering or looking for Step Definitions.

Also check out Multiline Step Arguments for more info on how to pass entire tables or bigger strings to your step definitions.

55 Step definitions

Successful steps When Cucumber finds a matching Step Definition it will execute it. If the block in the step definition doesn’t raise an Exception, the step is marked as successful (green). What you return from a Step Definition has no significance what so ever.

Undefined steps When Cucumber can’t find a matching Step Definition the step gets marked as yellow, and all subsequent steps in the scenario are skipped. If you use --strict this will cause Cucumber to exit with 1.

56 Step definitions

Pending steps When a Step Definition’s Proc invokes the pending method, the step is marked as yellow (as with undefined ones), reminding you that you have work to do. If you use --strict this will cause Cucumber to exit with 1.

Failed steps When a Step Definition’s Proc is executed and raises an error, the step is marked as red. What you return from a Step Definition has no significance what so ever. Returning nil or false will not cause a step definition to fail.

57 Step definitions

Skipped steps Steps that follow undefined, pending or failed steps are never executed (even if there is a matching Step Definition), and are marked cyan. String steps Steps can be defined using strings rather than regular expressions. Instead of writing Given /^I have (.*) cucumbers in my belly$/ do |cukes| You could write Given "I have $count cucumbers in my belly" do |cukes| Note that a word preceded by a $ sign is taken to be a placeholder, and will be converted to match .*. The text matched by the wildcard becomes an argument to the block, and the word that appeared in the step definition is disregarded.

58 Step definitions

Ambiguous steps Consider these step definitions: Given /Three (.*) mice/ do |disability| # some code end Given /Three blind (.*)/ do |animal| # some other code.. end And a plain text step: Given Three blind mice Cucumber can’t make a decision about what Step Definition to execute, and will raise a Cucumber::Ambiguous error telling you to fix the ambiguity.

59 Step definitions

Guess mode Running the plain text step will match the Regexp of both step definitions and raise Cucumber::Ambiguous. However, if you run Cucumber with --guess, it will guess that you were aiming for the step definition with 2 match groups. There is ranking logic that gets invoked when the option is turned on: The longest Regexp with 0 capture groups always wins. The Regexp with the most capture groups wins (when there are none with 0 groups). If there are 2+ Regexen with the same number of capture groups, the one with the shortest overall captured string length wins. If there are still 2+ options then an Ambiguous error is raised.

60 Step definitions

Guess mode So if you try --guess with the mice above, Cucumber will pick /Three blind (.*)/, because “mice” is shorter than “blind”.

Consider guess mode a workaround. We still recommend you try to have unambiguous regular expressions. When you have a lot of step definitions you quickly lose track of the situations where cucumber will apply guessing logic, and that can lead to some surprises.

61 Step definitions

Redundant Step Definitions In Cucumber you’re not allowed to use a regexp more than once in a Step Definition (even across files, even with different code inside the Proc), so the following would cause a Cucumber::Redundant error: Given /Three (.*) mice/ do |disability| # some code end Given /Three (.*) mice/ do |disability| # some other code.. end

62 Feedback for Invalid Credit Card

Feature: Feedback when entering invalid credit card details

In user testing we've seen a lot of people who made mistakes entering their credit card. We need to be as helpful as possible here to avoid losing users at this crucial stage of the transaction.

Background: Given I have chosen some items to buy And I am about to enter my credit card details

63 Example

Scenario: Credit card number too short When I enter a card number that's only 15 digits long And all the other details are correct And I submit the Then the form should be redisplayed And I should see a message advising me of the correct number of digits

Scenario: Expiry date invalid When I enter a card expiry date that's in the past And all the other details are correct And I submit the form Then the form should be redisplayed And I should see a message telling me the expiry date must be wrong

64 Example

Scenario: Credit card number too short When I enter a card number that's only 15 digits long And all the other details are correct And I submit the form Then the form should be redisplayed And I should see a message advising me of the correct number of digits

Scenario: Expiry date invalid When I enter a card expiry date that's in the past And all the other details are correct And I submit the form Then the form should be redisplayed And I should see a message telling me the expiry date must be wrong

65 Cash_Withdrawal

Feature: Cash Withdrawal Scenario: Successful withdrawal from an account in credit Given I have deposited $100 in my account When I request $20 Then $20 should be dispensed

66 Cash_Withdrawal

Given(/^I have deposited \$(\d+) in my account$/) do |amount| Account.new(amount.to_i) end

When(/^I request \$(\d+)$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end

Then(/^\$(\d+) should be dispensed$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end

67 Cash_Withdrawal

SurendraMac$ cucumber Feature: Cash Withdrawal

Scenario: Successful withdrawal from an account in credit # features/cash_withdrawal.feature:2 Given I have deposited $100 in my account # features/step_definitions/steps.rb:1 uninitialized constant Account (NameError) ./features/step_definitions/steps.rb:2:in `/^I have deposited \$(\d+) in my account$/' features/cash_withdrawal.feature:3:in `Given I have deposited $100 in my account' When I request $20 # features/step_definitions/steps.rb:5 Then $20 should be dispensed # features/step_definitions/steps.rb:9

Failing Scenarios: cucumber features/cash_withdrawal.feature:2 # Scenario: Successful withdrawal from an account in credit

1 scenario (1 failed) 3 steps (1 failed, 2 skipped) 0m0.017s

68 Cash_Withdrawal class Account def initialize(amount) end end

Given(/^I have deposited \$(\d+) in my account$/) do |amount| Account.new(amount.to_i) end

When(/^I request \$(\d+)$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end

69 Cash_Withdrawal

SurendraMac$ cucumber Feature: Cash Withdrawal

Scenario: Successful withdrawal from an account in credit # features/ cash_withdrawal.feature:2 Given I have deposited $100 in my account # features/step_definitions/steps.rb:6 When I request $20 # features/step_definitions/steps.rb:10 TODO (Cucumber::Pending) ./features/step_definitions/steps.rb:11:in `/^I request \$(\d+)$/' features/cash_withdrawal.feature:4:in `When I request $20' Then $20 should be dispensed # features/step_definitions/steps.rb:14

1 scenario (1 pending) 3 steps (1 skipped, 1 pending, 1 passed) 0m0.016s Surendras-MacBook-Pro:cash_withdrawal SurendraMac$

70 Cash_Withdrawal

There are a couple of things we’re not happy about: • There’s some inconsistent language creeping in; the step talks about depositing funds into the account, but the code passes the funds to the Account class’s constructor.

• The step is lying to us! It says Given I have deposited $100 in my account, and it’s passed. Yet we know from our implementation that nothing has been deposited anywhere.

• Having to convert the amount to an integer is messy. If we have a variable called amount, we should expect it to already be a number of some kind, not the string captured by the regular expression.

71 Cash_Withdrawal

. Getting the Words Right We want to clarify the wording before we do anything else, so let’s think about how we could make the code in the step definition read more like the text in the step. We could go back and reword the step to say something like Given an Account with a balance of $100. In reality, though, the only way that an account would have a balance is if someone deposited funds into it. So, let’s change the way we talk to the domain model inside our step definition to reflect that:

72 Cash_Withdrawal

So, let’s change the way we talk to the domain model inside our step definition to reflect that: class Account def deposit(amount) end end Given /^I have deposited \$(\d+) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount.to_i) end

73 Cash_Withdrawal

Telling the Truth Now that we’re happier with the interface to our Account class, we can resolve the next issue from our code review. After we’ve deposited the funds in the account, we can check its balance with an assertion:

Given /^I have deposited \$(\d+) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount.to_i) my_account.balance.should eq(amount.to_i), "Expected the balance to be #{amount} but it was #{my_account.balance}” end

We’ve used an RSpec assertion here. It might seem odd to put an assertion in a Given step, but it communicates to future readers of this code what state we expect the system to be in once the step has run.

74 Cash_Withdrawal

We’ll need to add a balance method to the Account so that we can run this code: class Account def deposit(amount) end def balance end end

75 Cash_Withdrawal class Account def deposit(amount) end def balance end end

Given(/^I have deposited \$(\d+) in my account$/) do |amount| my_account = Account.new my_account.deposit(amount.to_i) my_account.balance.should eq (amount.to_i), "Expected the balance to be #{amount} but it was #{my_account.balance}" end

76 Cash_Withdrawal

cucumber Feature: Cash Withdrawal

Scenario: Successful withdrawal from an account in credit # features/cash_withdrawal.feature:2 Given I have deposited $100 in my account # features/step_definitions/steps.rb:8 wrong number of arguments (2 for 1) (ArgumentError) ./features/step_definitions/steps.rb:11:in `/^I have deposited \$(\d+) in my account$/' features/cash_withdrawal.feature:3:in `Given I have deposited $100 in my account' When I request $20 # features/step_definitions/steps.rb:15 Then $20 should be dispensed # features/step_definitions/steps.rb:19

Failing Scenarios: cucumber features/cash_withdrawal.feature:2 # Scenario: Successful withdrawal from an account in credit

1 scenario (1 failed) 3 steps (1 failed, 2 skipped) 0m0.025s

77 Cash_Withdrawal

Now our step definition is much more robust, because we know it will sound an alarm bell if it isn’t able to deposit the funds into the account as we’ve asked it to do. Adding assertions to Given and When steps like this means that if there’s ever a regression later in the project, it’s much easier to diagnose because the scenario will fail right where the problem occurs. This technique is most useful when you’re sketching things out; eventually, we’ll probably move this check further down the testing stack into a unit test for the Account and take it out of the step definition.

78 Cash_Withdrawal class Account def deposit(amount) @balance = amount end def balance @balance end end

79 Cash_Withdrawal

Removing Duplication with Transforms Another issue we have with this step definition is that we have to convert the string captured by the regular expression into an integer. In fact, now that we’ve added an assertion, we’ve had to do it twice. As our test suite grows, we can imagine these calls to to_i littering our step definitions. Even these four characters count as duplication, so let’s stamp them out. To do this, we’re going to learn about a new Cucumber method, called Transform. Transforms work on captured arguments. Each transform is responsible for converting a certain captured string and turning it into something more meaningful. For example, we can use this transform to take a matched argu- ment that contains a number and turn it into a Ruby Fixnum integer: Transform /^\d+$/ do |number| number.to_i end

80 Cash_Withdrawal

Removing Duplication with Transforms ( cont..) We define the transform by giving Cucumber a regular expression that describes the argument we’re interested in transforming. Notice that we’ve used the ^ and $ to anchor the transform’s regular expression to the ends of the captured string. As we want our transform to match only captures that are numbers, not just captures that contain a number somewhere in them. When Cucumber matches a step definition, it checks for any transforms that match each argument. When an argument matches a transform, Cucumber passes the captured string to the transform’s block, and the result of running the block is what’s then yielded to the step definition.

81 Cash_WithdrawalRemoving Duplication with Transforms • 119

Step Transforms: how do they work?

nition Regular Expression

Captured Captured ... Argument Argument

Transform Transform ...

nition Ruby Code Block

Figure 6—Transforms: how do they work? 82

Download step_definitions_inside/08/features/step_definitions/steps.rb CAPTURE_CASH_AMOUNT = Transform /^\$(\d+)$/ do |digits| digits.to_i end

Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount) my_account.balance.should eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}" end

Notice that we’ve used a capture group inside the transform to separate the numbers from the currency symbol. This is how we tell Cucumber that we’re interested in transforming only that part of the capture we’ve been passed, so that’s all that will be passed into our block. If we wanted to capture the currency symbol as well, we could put another capture group around it, and it would be yielded to the transform’s block as another argument:

www.it-ebooks.info report erratum • discuss Cash_Withdrawal

With the transform in place, we can now remove the duplicated calls to to_i in our step definition:

Given /^I have deposited \$(\d+) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount) my_account.balance.should eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}” end

83 Cash_Withdrawal

The regular expression that we use to capture the number is now duplicated: we have \d+ both in the step definition and in the transform’s definition. This could be a problem if we wanted, for example, to start using cents as well as dollars in our features; we’d have to change the regular expression in the step definition and in the transform. Fortunately, Cucumber allows us to define the regular expression once, in the transform, and then reuse it in the step definition, like this:

CAPTURE_A_NUMBER = Transform /^\d+$/ do |number| number.to_i end

84 Cash_Withdrawal

CAPTURE_A_NUMBER = Transform /^\d+$/ do |number| number.to_i end Given /^I have deposited \$(#{CAPTURE_A_NUMBER}) in my account$/ do | amount| my_account = Account.new my_account.deposit(amount) #my_account.balance.should eq(amount), expect(my_account.balance).to eq(amount) "Expected the balance to be #{amount} but it was #{my_account.balance}" end

85 Cash_Withdrawal

We store the result of calling Transform in the constant CAPTURE_A_NUMBER and then use that constant as we build the regular expression in the step definition. As well as making it easier to change and reuse this capturing regular expression in the future, this refactoring makes it more obvious to someone reading the step definition that this argument will be transformed. We can tidy this up a little further by moving the dollar sign into the trans- form’s capture. This makes the code more cohesive, because we’re bringing together the whole regular expression statement for capturing the amount of funds deposited. It also gives us the option to capture other currencies in the future.

86 Cash_Withdrawal

CAPTURE_CASH_AMOUNT = Transform /^\$(\d+)$/ do |digits| digits.to_i end Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount) my_account.balance.should eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}” end

87 Cash_Withdrawal class Account def deposit(amount) @balance = amount end

def balance @balance end end

CAPTURE_CASH_AMOUNT = Transform /^\$(\d+)$/ do |digits| digits.to_i end Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount) my_account.balance.should eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}" end

88 Cash_Withdrawal

SurendraMac$ cucumber Feature: Cash Withdrawal

DEPRECATION: Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }` instead. Called from /Users/SurendraMac/Cucumber/cash_withdrawal/features/step_definitions/steps.rb:18:in `block in '. Scenario: Successful withdrawal from an account in credit # features/cash_withdrawal.feature:2 Given I have deposited $100 in my account # features/step_definitions/steps.rb:15 When I request $20 # features/step_definitions/steps.rb:22 TODO (Cucumber::Pending) ./features/step_definitions/steps.rb:23:in `/^I request \$(\d+)$/' features/cash_withdrawal.feature:4:in `When I request $20' Then $20 should be dispensed # features/step_definitions/steps.rb:26

1 scenario (1 pending) 3 steps (1 skipped, 1 pending, 1 passed) 0m0.027s Surendras-MacBook-Pro:cash_withdrawal SurendraMac$

89 Cash_Withdrawal

If we wanted to capture the currency symbol as well, we could put another capture group around it, and it would be yielded to the transform’s block as another argument: Transform /^(£|\$|€)(\d+)$/ do | currency_symbol, digits | Currency::Money.new(digits, currency_symbol) end Using the transform has cleared up the final point from the initial code review. As we went along, we collected a new to-do list item: that we need to implement the Account properly, with unit tests.

90 Cash_Withdrawal

Adding Custom Helper Methods to the World We’ve implemented the first step of our scenario to set up an account with a sufficient balance that the withdrawal should work. Let’s check another step When I request $20 # features/step_definitions/steps.rb:22 TODO (Cucumber::Pending) ./features/step_definitions/steps.rb:23:in `/^I request \$(\d+)$/' features/cash_withdrawal.feature:4:in `When I request $20' Then $20 should be dispensed # features/step_definitions/steps.rb:26

The first step is passing as we’d expect, and the second one is failing with a pending message. So, our next task is to implement the step to simulate a customer withdrawing cash from the ATM. Here’s the empty step definition:

When /^I request \$(\d+)$/ do |arg1| pending # express the regexp above with the code you wish you had end

91 Cash_Withdrawal

When /^I request \$(\d+)$/ do |arg1| pending # express the regexp above with the code you wish you had end First we can reuse the transform we created earlier to translate the cash amount, captured as a string by Cucumber, into a number: When /^I request (#{CAPTURE_CASH_AMOUNT})$/ do |amount| pending # express the regexp above with the code you wish you had end

92 Cash_Withdrawal

We need somewhere to withdraw the cash from, which means we need to bring a new actor into this scenario. This new class is going to handle our request to withdraw cash from our account. If we walked into a bank in real life, that role would be played by a teller. Thinking outside-in again, let’s sketch out the code we’d like: When /^I request (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(my_account, amount) end

93 Cash_Withdrawal

The teller will need to know which account to take the cash from and how much to take. There’s a little inconsistency creeping into the language again, though: the step definition talks about requesting the cash, but in the code we’re withdrawing it. Withdraw is the term we use most often, so let’s change the text in the scenario to match.

Feature: Cash Withdrawal Scenario: Successful withdrawal from an account in credit Given I have deposited $100 in my account When I withdraw $20 Then $20 should be dispensed

94 Cash_Withdrawal

You can implement step definitions for undefined steps with these snippets: When(/^I withdraw \$(\d+)$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end Let’s modify above code:

When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(my_account, amount) end

95 Cash_Withdrawal

Run cucumber again, and you should be prompted to create a Teller class. Let’s create one: When I withdraw $20 # features/step_definitions/steps.rb:23 uninitialized constant Teller (NameError) ./features/step_definitions/steps.rb:24:in `/^I withdraw (\$(?:\d+))$/' features/cash_withdrawal.feature:4:in `When I withdraw $20' Then $20 should be dispensed # features/step_definitions/steps.rb:28

Failing Scenarios: cucumber features/cash_withdrawal.feature:2 # Scenario: Successful withdrawal from an account in credit

1 scenario (1 failed) 3 steps (1 failed, 1 skipped, 1 passed) 0m0.017s

96 Cash_Withdrawal

Run cucumber again, and you should be prompted to create a Teller class. Let’s create one: class Teller def withdraw_from(account, amount) end end

When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(my_account, amount) end

97 Cash_Withdrawal

When I withdraw $20 # features/step_definitions/steps.rb:27 undefined local variable or method `my_account' for # (NameError) ./features/step_definitions/steps.rb:29:in `/^I withdraw (\$(?:\d+))$/' features/cash_withdrawal.feature:4:in `When I withdraw $20' Then $20 should be dispensed # features/step_definitions/steps.rb:32

Failing Scenarios: cucumber features/cash_withdrawal.feature:2 # Scenario: Successful withdrawal from an account in credit

1 scenario (1 failed) 3 steps (1 failed, 1 skipped, 1 passed) 0m0.018s Surendras-MacBook-Pro:cash_withdrawal SurendraMac$

98 Cash_Withdrawal

We defined my_account in the first step definition, but when we tried to use it in the second step definition, Ruby can’t see it. How can we make my_account available to both step definitions? The answer lies in understanding something fundamental about how Cucumber executes step definitions. Storing State in the World Just before it executes each scenario, Cucumber creates a new object. We call this object the World. The step definitions for the scenario execute in the context of the World, effectively as though they were methods of that object. Just like methods on a regular Ruby class, we can use instance variables to pass state between step definitions.

99 Cash_Withdrawal

Here’s how the code looks with my_account stored as an instance variable in the step definition: Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| @my_account = Account.new @my_account.deposit(amount) @my_account.balance.should eq(amount), "Expected the balance to be #{amount}" end When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(@my_account, amount) end

100 Cash_Withdrawal

Creating Custom Helper Methods In a regular class we’d avoid nils by putting the instance variables behind an accessor method, like this: def my_account @my_account ||= Account.new end Using ||= ensures that the new Account will be created only once and then stored in an instance variable. You can do the same in Cucumber. To add custom methods to the World, you define them in a module and then tell Cucumber you want them to be mixed into your World.

101 Cash_Withdrawal module KnowsMyAccount def my_account @my_account ||= Account.new end end World(KnowsMyAccount) Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| my_account = Account.new my_account.deposit(amount) # my_account.balance.should eq(amount), expect(my_account.balance).to eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}" end end

102 Cash_Withdrawal

We defined my_account on a module called KnowsMyAccount and then told Cucumber to mix that module into our World by calling Cucumber’s special World method. As usual, we’ve done all this inline in the step definition file for now, but we’re going to tidy it away soon. This means we can get rid of the code that initializes the Account from the first step definition, and we can get rid of the instance variable, so the step definitions go back to using my_account again: Given /^I have deposited (#{CAPTURE_CASH_AMOUNT}) in my account$/ do |amount| my_account.deposit(amount) my_account.balance.should eq(amount), "Expected the balance to be #{amount} but it was #{my_account.balance}" end When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(my_account, amount) end

103 Cash_Withdrawal class Teller def withdraw_from(account, amount) end end When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new teller.withdraw_from(my_account, amount) Then /^\$(\d+) should be dispensed$/ do |arg1| pending # express the regexp above with the code you wish you had end

104 Cash_Withdrawal

$cucumber Feature: Cash Withdrawal DEPRECATION: Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling … Scenario: Successful withdrawal from an account in credit # features/cash_withdrawal.feature:2 Given I have deposited $100 in my account # features/step_definitions/steps.rb:26 When I withdraw $20 # features/step_definitions/steps.rb:38 Then $20 should be dispensed # features/step_definitions/steps.rb:43 TODO (Cucumber::Pending) ./features/step_definitions/steps.rb:44:in `/^\$(\d+) should be dispensed$/' features/cash_withdrawal.feature:5:in `Then $20 should be dispensed' 1 scenario (1 pending) 3 steps (1 pending, 2 passed) 0m0.017s

105 Cash_Withdrawal

This time my_account won’t use a local variable in the step definition, but it will call our helper method instead. With this change in place, run cucumber, and everything should be passing.

Customizing the World

Our second step is passing now, so let’s take a break from the example to learn a bit more about the World.

The main way you’ll use the World is by extending it with modules of code that support your step definitions by performing common actions against your system: module MyCustomHelperModule

def my_helper_method ... end end

World(MyCustomHelperModule)

106 Cash_Withdrawal

In this case, you’ll be extending a World object that Cucumber has created for you. By default, Cucumber creates each World by simply calling Object.new, but you can override this and use a different class if you need to, by passing a block to the World method: class MySpecialWorld def some_helper_method end end World { MySpecialWorld.new }

Whatever class you use for your worlds, Cucumber always mixes in the module Cucumber::RbSupport::RbWorld, which contains various helper methods you can use to talk to Cucumber from your step definitions.

107 Cash_Withdrawal

Designing Our Way to the Finish Line Back in the real world, we were trying to get our final step to pass. When we run cucumber, we can see that the first two steps are passing, and the final one is pending. Almost there! Let’s take a look at that last step definition: Then /^\$(\d+) should be dispensed$/ do |arg1| pending # express the regexp above with the code you wish you had end The big question here is: where will the cash be dispensed? Which part of the system can we examine for evidence of whether it doled out the readies? It seems as if we’re missing a domain concept. In the physical ATM, the cash will end up poking out of a slot on the ATM, something like this: Then /^(#{CAPTURE_CASH_AMOUNT}) should be dispensed$/ do |amount| expect(cash_slot.contents).to eq(amount) end

108 Cash_Withdrawal

Designing Our Way to the Finish Line That looks good. When we hook our code up to the real hardware, we’re going to need some way of talking to it, and this object will work fine as a test double in the meantime. Let’s start running cucumber to drive this out. First it tells us that we need to define the cash_slot method, of course. Let’s add that method to our helper module and rename the module to reflect its new role. module KnowsTheDomain def my_account @my_account ||= Account.new end def cash_slot @cash_slot ||= CashSlot.new end end World(KnowsTheDomain)

109 Cash_Withdrawal

Designing Our Way to the Finish Line We run cucumber again, and this time it wants us to define the CashSlot class, so let’s go ahead and do as we’re told. Again, we just build a sketch of the interface we want to use, with minimal implementation: class CashSlot def contents raise("I'm empty!") end end

110 Cash_Withdrawal

Designing Our Way to the Finish Line Now when you run cucumber, you’ll see we’ve moved closer to our goal: all the classes and methods are wired up, and our final step is failing just because there’s no cash coming out of the CashSlot: Feature: Cash Withdrawal Scenario: Successful withdrawal from an account in credit Given I have deposited $100 in my account When I withdraw $20 Then $20 should be dispensed I’m empty! (RuntimeError) ./features/step_definitions/steps.rb:19 ./features/step_definitions/steps.rb:55 features/cash_withdrawal.feature:5 Failing Scenarios: cucumber features/cash_withdrawal.feature:2 1 scenario (1 failed) 3 steps (1 failed, 2 passed) 0m0.004s

111 Cash_Withdrawal

Designing Our Way to the Finish Line To get this last step to pass, someone needs to tell the CashSlot to dispense the cash when the customer makes a withdrawal. It’s the Teller who’s in charge of the transaction, but at the moment he doesn’t know anything about the CashSlot. We’ll use dependency injection to pass the CashSlot in to Teller’s constructor. Now we can imagine a new CashSlot method that the Teller can use to tell it to dispense the cash: class Teller def initialize(cash_slot) @cash_slot = cash_slot end def withdraw_from(account, amount) @cash_slot.dispense(amount) end end

112 Cash_Withdrawal

Designing Our Way to the Finish Line This seems like the simplest implementation of Teller that we need to get the scenario to pass. It’s odd that when we designed this method from the outside we thought we’d need the account parameter, but now we don’t seem to need it. Let’s stay focused, though: we’ll make a note on our to-do list to look into this later and carry on getting this step to pass. There are two changes we need to make now. We need to add the new dispense method to CashSlot, and we have to change the second step definition to create the Teller correctly:

When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller = Teller.new(cash_slot) teller.withdraw_from(my_account, amount) end This call to Teller.new seems out of place in a step definition now

113 Cash_Withdrawal

Designing Our Way to the Finish Line 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)

114 Cash_Withdrawal

Designing Our Way to the Finish Line When /^I withdraw (#{CAPTURE_CASH_AMOUNT})$/ do |amount| teller.withdraw_from(my_account, amount) end The step definition code all reads very nicely now. Pushing some of the details down into our World module means the step definition code is at a higher level of abstraction. This makes it less of a mental leap when you come into the step definitions from a business- facing scenario, because the code doesn’t contain too much detail.

115 Cash_Withdrawal

Designing Our Way to the Finish Line This scenario isn’t going to pass until we do some work on our CashSlot, though. Run cucumber, and you’ll see that it’s missing the dispense method. A simple implementation of CashSlot should get this working: class CashSlot def contents @contents or raise("I'm empty!") end def dispense(amount) @contents = amount end end

116 Cash_Withdrawal

Run cucumber one last time, and you should see the scenario pass: Feature: Cash Withdrawal Scenario: Successful withdrawal from an account in credit Given I have deposited $100 in my account When I withdraw $20 Then $20 should be dispensed 1 scenario (1 passed) 3 steps (3 passed) 0m0.002s Excellent! Go and grab a drink—then we can sit down, review the code, and do some refactoring.

117 Conclusion

Cucumber is BDD framework useful for writing project requirement in 40 different languages, where we can involve stakeholder and for development of exact expectation of our customer. It is Gherkin syntax for writing business scenario and ruby/ other language for implementation of all scenarios.

118 Reference Book

119 Watir

Watir ( Testing in Ruby, pronounced water), is an open-source (BSD) family of Ruby libraries for automating web browsers. It drives , , Chrome, and , and is available as a RubyGems gem. Watir was primarily developed by Bret Pettichord and Paul Rogers.

120 Watir

Watir, pronounced water, is an open-source (BSD) family of Ruby libraries for automating web browsers. It allows you to write tests that are easy to read and maintain. It is simple and flexible. Watir drives browsers the same way people do. It clicks links, fills in forms, presses buttons. Watir also checks results, such as whether expected text appears on the page. Watir is a family of Ruby libraries but it supports your app no matter what technology it is developed in. Whilst Watir supports only Internet Explorer on Windows, Watir-WebDriver supports Chrome, Firefox, Internet Explorer, Opera and also running in headless mode (HTMLUnit).

121 Why Watir?

It’s a free Open Source tool. There are no costs to use the tool. There’s a very active and growing community behind it. It uses Ruby, a full-featured modern scripting language, rather than a proprietary vendor script. It supports your web app no matter what it is developed in. It supports multiple browsers on different platforms. It is powerful and easy to use, yet beautifully lightweight. Don’t just take our word for it, see some awesome quotes below and install Watir.

122 Watir gem relationships

123 Watir gem relationships

Watir is really three gems (or projects) that work together to drive the different browser types: Watir – This gem is just a loader. Based on the browser that you want to run and your , the watir gem will either load watir-classic or watir-webdriver. Watir-Classic – This is original watir gem that only drives Internet Explorer. It is recommended to use this gem (instead of watir-webdriver) for IE due to its stability. Watir-Webdriver – This gem allows driving of additional browsers – eg Chrome and Firefox. It is technically just a API wrapper around the selenium-webdriver gem (ie translates the watir commands to selenium-webdriver commands).

124 Watir Users

125 Functionality

Watir project consists of several smaller projects. The most important ones are watir-classic, watir-webdriver and watirspec.

126 Functionality

Watir-classic • Watir-classic makes use of the fact that Ruby has built in Object Linking and Embedding (OLE) capabilities. • As such it is possible to drive Internet Explorer programmatically. • Watir-classic operates differently than HTTP based test tools, which operate by simulating a browser. • Instead Watir-classic directly drives the browser through the OLE protocol, which is implemented over the Component Object Model (COM) architecture. • The COM permits interprocess communication (such as between Ruby and Internet Explorer) and dynamic object creation and manipulation (which is what the Ruby program does to the Internet Explorer). • Microsoft calls this OLE automation, and calls the manipulating program an automation controller. • Technically, the Internet Explorer process is the server and serves the automation objects, exposing their methods; while the Ruby program then becomes the client which manipulates the automation objects.

127 Functionality

Watir-webdriver Watir-webdriver is a modern version of the Watir API based on Selenium. Selenium 2.0 (selenium-webdriver) aims to be the reference implementation of the WebDriver specification. In Ruby, Jari Bakken has implemented the Watir API as a wrapper around the Selenium 2.0 API. Not only is Watir-webdriver derived from Selenium 2.0, it is also built from the HTML specification, so Watir-webdriver should always be compatible with existing W3C specifications. Watirspec Watirspec is executable specification of the Watir API, like RubySpec is for Ruby.

128 Installation

Watir-webdriver gem install watir-webdriver or just add watir-webdriver to your gemfile if you rock and use bundler (if you don’t use bundler, you should, right now).

Watir-WebDriver ♥s ruby 1.9.3, but it won’t complain in older versions nonetheless Cucumber + Watir-WebDriver are a match made in heaven. They work really well together to automate your web app testing with a business domain specific specification layer

129 How Watir Work?

130 Let’s try

Including Watir gem to drive Internet Explorer on Windows require 'watir' Including Watir-WebDriver gem to drive Firefox/Chrome on Windows/ Mac/Linux require 'watir-webdriver' Starting a new browser & and going to our site browser = Watir::Browser.new browser.goto ‘http://bit.ly/watir-example'

131 Let’s try

Setting a text field

Text Field browser.text_field(:name => 'entry.0.single').set 'Watir'

132 Let’s try

Setting a multi-line text box

Text Box browser.text_field(:name => 'entry.1.single').set "I come here from India. \n The weather is great here."

133 Let’s try

Setting and clearing a radio button

Radio Buttons browser.radio(:value => 'Watir').set browser.radio(:value => ‘Watir').clear

134 Setting and clearing check boxes

Checkboxes browser.checkbox(:value => 'Ruby').set browser.checkbox(:value => 'Python').set browser.checkbox(:value => 'Python').clear

135 Clicking a button

browser.button(:name => 'logon').click

Clearing, getting and selecting selection list values

Selection List browser.select_list(:name => 'entry.6.single').clear puts browser.select_list(:name => 'entry.6.single').options browser.select_list(:name => 'entry.6.single').select 'Chrome'

136 How Watir Work?

Clicking a button

Button browser.button(:name => 'submit').click

Checking for text in a page puts browser.text.include? 'Your response has been recorded.'

Checking the title of a page puts browser.title == 'Thanks!'

137 watir_ex1.rb require 'watir-webdriver' b = Watir::Browser.new b.goto 'bit.ly/watir-webdriver-demo' b.text_field(:id => 'entry_1000000').set 'your name' b.select_list(:id => 'entry_1000001').select 'Ruby' b.select_list(:id => 'entry_1000001').selected? 'Ruby' b.button(:name => 'submit').click b.text.include? 'Thank you'

138 Watir Related Libraries

Integration watir-rails for using Watir with Rails. watir-rspec for writing your tests with RSpec. watir-robot for using Watir with Robot Framework. watir-timecop for making Watir compatible with Timecop.

Frameworks page-object test-page test-factory watirmark watirsome

139 Watir Related Libraries

Other watir-webdriver-performance for monitoring web application performance. watir-scroll for scrolling emulation watir-dom-wait for element waiting based on DOM change watir-get-image-content for getting image content watir-extensions-element-screenshot for making screenshot of specific element

140 Watir Related Libraries

Add it into your Gemfile: gem "watir", "~>4.0" Or install it manually with the following command: gem install watir Then in your test file: require "watir" browser = Watir::Browser.new

The command above will use watir-classic with Internet Explorer on Windows and watir-webdriver with Firefox on Linux/OS X by default.

141 Switching Between Drivers

There are multiple ways to specify a different driver. By specifying different browser via #initialize: require "watir" # will use watir-webdriver with Chrome browser = Watir::Browser.new :chrome By specifying driver manually via Watir.driver#=: # will use watir-webdriver with Internet Explorer Watir.driver = :webdriver browser = Watir::Browser.new :ie Or by using an environment variable WATIR_DRIVER: # will use watir-classic ENV["WATIR_DRIVER"] = "classic" browser = Watir::Browser.new

142 Watir Examples

Here are some examples demonstrating basic usage. # create new firefox instance b = Watir::Browser.new :firefox

# navigation b.goto "http://newtours.demoaut.com"

# check if textbox exists using b.text_field(:name => "userName").exists?

# set value b.text_field(:name => "userName").set "test" b.text_field(:name => "password").set "test"

143 Watir Examples

# click login b.button(:name => "login").click

# radio b.radio(:value => "oneway").set # is this value selected? puts b.radio(:value => "oneway").set?

# listbox b.select_list(:name => "fromPort").select "Portland"

144 Watir Examples

# verify if Portland selected puts b.select_list(:name => "fromPort").selected?("Portland") b.radio(:value => "Business").set puts b.radio(:value => "Business").set?

# div: when its present, output text puts b.div(:class => "footer").when_present.text

# screenshot b.screenshot.png b.screenshot.save "file.png"

145 Watir Examples

Singular vs Plural This is a very neat feature – use singular to find the first occurence of the object or use plural to find all its occurences. b.div # returns the first div b.divs # returns a collection of div b.image # returns the first img b.images # returns a collection of img b.table(:id => "table").images # images inside a table b.divs(:class => "test") # all divs with class => test

146 Watir Examples

require "watir" ie = Watir::IE.new #create an object to drive the browser ie.goto "http://www.google.com/" ie.url == "http://www.google.com/" ie.link(:text, "Images").flash #flash the item text "Images" ie.link(:text, "Images").click #click on the link to the images search page ie.text.include? "The most comprehensive image search on the web" #test to make sure it worked searchTerm = "kittens" #set a variable to hold our search term ie.text_field(:name, "q").set(searchTerm) # q is the name of the search field ie.button(:name, "btnG").click # "btnG" is the name of the google button if ie.contains_text(searchTerm) puts "Test Passed. Found the test string: #{searchTerm}. Actual Results match Expected Results." else puts "Test Failed! Could not find: #{searchTerm}" end

147 Watir Examples

#Watir script to show clicking a JavaScript popup box require "watir" require 'watir\contrib\enabled_popup' require 'startClicker' require 'net/http' require 'net/https'

$ie = Watir::IE.new #create an object to drive the browser $ie.goto "http://mydomain.com/ListGroups.aspx" if $ie.contains_text("Log In") $ie.text_field(:name, "Login1$UserName").set("fincherm") $ie.text_field(:name, "Login1$Password").set("mitch") $ie.button(:name, "Login1$LoginButton").click end

148 Watir Examples setDdlPriority("2") setDdlPriority("9") $ie.link(:text, "Baseline").click $ie.link(:text, "MoonManC").click def setDdlPriority(priority) ddlPriority = $ie.select_list( :name , /ddlPriority/) puts ddlPriority ddlPriority.select(priority) puts ddlPriority $ie.button(:name, "ctl00$btnSave").click_no_wait startClicker( "OK", 4 , "User Input" ) sleep 1 end

149 Watir Examples startClicker.rb: #method startClicker from http://wiki.openqa.org/display/WTR/FAQ def startClicker( button , waitTime= 9, user_input=nil ) # get a handle if one exists hwnd = $ie.enabled_popup(waitTime) if (hwnd) # yes there is a popup w = WinClicker.new if ( user_input ) w.setTextValueForFileNameField( hwnd, "#{user_input}" ) end

150 Watir Examples

# I put this in to see the text being input it is not necessary to work sleep 3 # "OK" or whatever the name on the button is w.clickWindowsButton_hwnd( hwnd, "#{button}" ) # # this is just cleanup w=nil end end

151 Introducing BDDfire

BDDfire • An instant Ruby-Cucumber BDD framework which supports various popular open-source libraries like , Selenium- WebDriver, Poltergeist, Relish, Cuke_sniffer, Rubocop, Appium, Saucelabs, Browserstack etc etc.BDDfire will create all the directories and required files to support the latest open-source libraries in the Ruby-Cucumber framework. • BDDfire is a and we can install and use it in any brand new Cucumber project or existing Cucumber framework. • BDDfire source code is available on the Github. In this post, we will explore BDDfire in the detail.

152 BDDfire Features

Cucumber is a very popular BDD framework. Cucumber become more effective when it’s plugged with other open -source libraries. Sometime we miss most of the useful libraries to be used with Ruby- Cucumber. BDDfire supports lots of modern open-source libraries so that we won’t miss new trends in the world of Ruby-Cucumber.

BDDfire has following features: • BDDfire will create template directories and files for the Cucumber project. • Capybara and WebDriver Integration for executing acceptance scenarios in the real browsers • Capybara and Poltergeist integration to execute scenarios in the headless browsers. • Generate template directories to support RSpec

153 BDDfire Features

• BDDfire supports cuke_sniffer and Rubocop libraries which detect smell in the Ruby-Cucumber framework. • BDDfire supports Relish for the living documentation. • Rakefile has been automatically created as a part of BDDfire • BDDfire supports YARD documentation of the cucumber project. • BDDfire allow you write steps using Page Object Pattern

154 BDDfire Installation

• With BDDfire, you can install Ruby-Cucumber BDD framework with all those libraries in a single command. $ gem install bddfire • Or include BDDfire in the Gemfile • gem 'bddfire' • Once installed we can make use of the BDDfire in our existing project. BDDfire commands will be listed when you run ‘bddfire’ • $ bddfire • Commands: • bddfire fire_cucumber • bddfire fire_rspec • bddfire generate_yard • bddfire help [COMMAND] • bddfire version

155 Installation

Surendras-MBP:RubyEx SurendraMac$ sudo gem install bddfire Fetching: require_all-1.3.3.gem (100%) Successfully installed require_all-1.3.3 Fetching: selenium-webdriver-2.44.0.gem (100%) Successfully installed selenium-webdriver-2.44.0 Fetching: rspec-support-3.2.2.gem (100%) Successfully installed rspec-support-3.2.2 Fetching: rspec-mocks-3.2.1.gem (100%) Successfully installed rspec-mocks-3.2.1 Fetching: rspec-expectations-3.2.1.gem (100%) Successfully installed rspec-expectations-3.2.1 Fetching: rspec-core-3.2.3.gem (100%) Successfully installed rspec-core-3.2.3 Fetching: rspec-3.2.0.gem (100%) Successfully installed rspec-3.2.0

156 Installation

Successfully installed rspec-3.2.0 Fetching: -2.0.0.gem (100%) Successfully installed xpath-2.0.0 Fetching: capybara-2.4.4.gem (100%) Successfully installed capybara-2.4.4 Fetching: gherkin-2.12.2.gem (100%) Building native extensions. This could take a while... Successfully installed gherkin-2.12.2 Fetching: cucumber-1.3.20.gem (100%) Successfully installed cucumber-1.3.20 Fetching: bddfire-1.9.7.gem (100%) Thanks for installing BDDfire gem. More exciting BDD stuff on the way. Stay Tuned ! Successfully installed bddfire-1.9.7

157 Installation

Parsing documentation for require_all-1.3.3 Installing ri documentation for require_all-1.3.3 Parsing documentation for selenium-webdriver-2.44.0 Installing ri documentation for selenium-webdriver-2.44.0 Parsing documentation for rspec-support-3.2.2 Installing ri documentation for rspec-support-3.2.2 Parsing documentation for rspec-mocks-3.2.1 Installing ri documentation for rspec-mocks-3.2.1 Parsing documentation for rspec-expectations-3.2.1 Installing ri documentation for rspec-expectations-3.2.1 Parsing documentation for rspec-core-3.2.3 Installing ri documentation for rspec-core-3.2.3

158 Installation

Installing ri documentation for rspec-3.2.0 Parsing documentation for xpath-2.0.0 Installing ri documentation for xpath-2.0.0 Parsing documentation for capybara-2.4.4 Installing ri documentation for capybara-2.4.4 Parsing documentation for gherkin-2.12.2 Installing ri documentation for gherkin-2.12.2 Parsing documentation for cucumber-1.3.20 Installing ri documentation for cucumber-1.3.20 Parsing documentation for bddfire-1.9.7 Installing ri documentation for bddfire-1.9.7 Done installing documentation for require_all, selenium-webdriver, rspec-support, rspec- mocks, rspec-expectations, rspec-core, rspec, xpath, capybara, gherkin, cucumber, bddfire after 45 seconds 12 gems installed

159 bddfire demo

Surendras-MBP:RubyEx SurendraMac$ mkdir bddfire_demo Surendras-MBP:RubyEx SurendraMac$ pwd /Users/SurendraMac/RubyEx Surendras-MBP:RubyEx SurendraMac$ cd bddfire_demo/ Surendras-MBP:bddfire_demo SurendraMac$ bddfire fire_cucumber ------Info------======CREATING 'cucumber' DIRECTORY WITH TEST FRAMEWORK======Copying all config files for cucumber projects======Copying Gemfile containing all required Gems======Copying cucumber.yml file with profiles of all Capybara Drivers======Copying pakage.json file with required npm dependencies======Copying README.md file with template usage======

160 bddfire demo

======Copying browser.json file with browsers and devices config for cloud testng======Copying .relish file, you need to add your relish api key======Copying .rubocop.yml file to check Ruby code======Copying c.ruby-version file ======Copying .travis.yml, you can delete if you are using other CI tool======Copying ..yml to generate YARD documentation for your project======Copying Rakefile to generate tasks ======------Info------All Config files created. ------

161 bddfire demo

------Info------======Copying features and support directories for cucumber projects======Copying Pages directories with Page Object Classes======Copying step_definitions directoy======Copying Suppoort directory with all Capybara driver setup======Creating env.rb and setting Selenium, Poltergeist, Saucelabs, Appium, browerstack drivers======Creating responsive.rb file to run scenarios with different viewports======------

162 bddfire demo

------Info------features and subdirectories created. ------Info------======Copying lib directory to put ruby config files for cucumber projects======------Info------lib subdirectory created. ------Info------======Copying lib directory to put ruby config files for cucumber projects======------Info------Scripts subdirectory created. ------

163 bddfire demo

Surendras-MBP:bddfire_demo SurendraMac$ ls cucumber Surendras-MBP:bddfire_demo SurendraMac$ cd cucumber/ Surendras-MBP:cucumber SurendraMac$ ls Gemfile Rakefile cucumber.yml lib script README.md browser.json features package.json Surendras-MBP:cucumber SurendraMac$ cat Gemfile source "http://rubygems.org" gem 'nokogiri', '1.6.1'

164 bddfire demo

gem 'cucumber' gem 'capybara' gem 'bddfire', "1.9.4" gem 'selenium-webdriver' gem 'poltergeist' gem 'rake' gem 'yard-cucumber' gem 'redcarpet' gem 'rubocop' gem 'cuke_sniffer' gem 'json' gem 'rspec' gem 'relish' gem 'rubocop-checkstyle_formatter' gem "parallel_tests" gem "chromedriver-helper", "0.0.6" Surendras-MBP:cucumber SurendraMac$ ls

165 bddfire demo

Surendras-MBP:cucumber SurendraMac$ ls Gemfile Rakefile cucumber.yml lib script README.md browser.json features package.json Surendras-MBP:bddfire_demo SurendraMac$ cd cucumber/ Surendras-MBP:cucumber SurendraMac$ ls Gemfile Rakefile cucumber.yml lib script README.md browser.json features package.json Surendras-MBP:cucumber SurendraMac$

166 bddfire demo

Surendras-MBP:cucumber SurendraMac$ cd features/ Surendras-MBP:features SurendraMac$ ls google.feature step_definitions pages support Surendras-MBP:features SurendraMac$ cat google.feature Feature: Google Search to explore BDDfire Scenario: View home page Given I am on "http://www.google.com" When I fill in "q" with the text "shashi" Then I should see "Sign in" Surendras-MBP:features SurendraMac$ cd ..

167 bddfire demo

Surendras-MBP:cucumber SurendraMac$ bddfire version 1.9.7 Surendras-MBP:cucumber SurendraMac$ ls Gemfile Rakefile cucumber.yml lib script README.md browser.json features package.json Surendras-MBP:cucumber SurendraMac$ bundle install Fetching gem metadata from http://rubygems.org/...... Fetching version metadata from http://rubygems.org/... Fetching dependency metadata from http://rubygems.org/.. Resolving dependencies....

168 bddfire demo

Surendras-MBP:cucumber SurendraMac$ bddfire version 1.9.7 Surendras-MBP:cucumber SurendraMac$ ls Gemfile Rakefile cucumber.yml lib script README.md browser.json features package.json Surendras-MBP:cucumber SurendraMac$ bundle install Fetching gem metadata from http://rubygems.org/...... Fetching version metadata from http://rubygems.org/... Fetching dependency metadata from http://rubygems.org/.. Resolving dependencies....

169