GKTCS Innovations Pvt. Ltd.

Ruby, Appium, Android, Rspec, , Automation Testing

Surendra Panpaliya

Director, GKTCS Innovations Pvt. Ltd, Pune.

16 + 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 ❑ Data Communication & Networking, CCNA ❑ UNIX /Linux Shell Scripting, System Programming ❑ CA Siteminder, Autosys, SSO, Service Desk, Service Delivery Author of 4 Books National Paper Presentation Awards at BARC Mumbai 2 Agenda ( Day 1)

• Introduction • Ruby installation • What’s New in Ruby • Ruby Data Types/ Data Structure • Ruby Comparison with Other Language ( Java/C+ + ) • Ruby Demo Script • Appium Introduction • Appium Installation Setup • Discussion

3 Agenda ( Day 2)

• Android adb commands • About Android AVD • About Appium Architecture • Appium Features • Using Appium to test Android Emulator/ Physical Device • Writing Ruby Scripts to Automate app through Appium • Discussion

4 Agenda ( Day 3)

• Manual Testing to Automation using Ruby • Execute and Automate Test • Rspec Introduction • Rspec Installation • Rspec for Automation • Rspec Demo • Rspec Best Practices • Pry Introduction • Debugging using Pry • Pry Demo • Discussion

5 Rspec

What is Rspec? • RSpec is a behavior-driven development (BDD) framework for the Ruby , inspired by JBehave. • It contains its own mocking framework that is fully integrated into the framework based upon JMock. • The framework can be considered a domain-specific language (DSL) and resembles a natural language specification.

6 Rspec

RSpec is a Behaviour-Driven Development tool for Ruby . BDD is an approach to software development that combines Test- Driven Development, Domain Driven Design,and Acceptance Test-Driven Planning. RSpec helps you do the TDD part of that equation,focusing on the documentation and design aspects of TDD.

7 Rspec

#Testing for our User class describe User do context 'with admin privileges' do before :each do @admin = Admin.get(1) end

it 'should exist' do expect(@admin).not_to be_nil end

it 'should have a name' do expect(@admin.name).not_to be_false end end

#... end

8 Rspec

Prerequisites

Ruby 1.8.7 or higher (2.0+ recommended) Install

$ gem install This installs five gems: rspec rspec-core rspec-expectations rspec-mocks rspec-support The rspec-core gem installs an rspec executable. Run the rspec command with the --help flag to see the available options:

9 rspec —help

Surendras-MacBook-Pro:~ SurendraMac$ rspec --help Usage: rspec [options] [files or directories]

-I PATH Specify PATH to add to $LOAD_PATH (may be used more than once). -r, --require PATH Require a file. -O, --options PATH Specify the path to a custom options file. --order TYPE[:SEED] Run examples by the specified order type. [defined] examples and groups are run in the order they are defined [rand] randomize the order of groups and examples [random] alias for rand [random:SEED] e.g. --order random:123 --seed SEED Equivalent of --order rand:SEED. --bisect[=verbose] Repeatedly runs the suite in order to isolate the failures to the smallest reproducible case. --[no-]fail-fast Abort the run on first failure. --failure-exit-code CODE Override the exit code used when there are failing specs. --dry-run Print the formatter output of your suite without running any examples or hooks

10 rspec —help

Surendras-MacBook-Pro:~ SurendraMac$ rspec --help **** Output ****

-f, --format FORMATTER Choose a formatter. [p]rogress (default - dots) [d]ocumentation (group and example names) [h]tml [j]son custom formatter class name -o, --out FILE Write output to a file instead of $stdout. This option applies to the previously specified --format, or the default format if no format is specified.

11 rspec —help

--deprecation-out FILE Write deprecation warnings to a file instead of $stderr. -b, --backtrace Enable full backtrace. -c, --[no-]color, --[no-]colour Enable color in the output. -p, --[no-]profile [COUNT] Enable profiling of examples and list the slowest examples (default: 10). -w, --warnings Enable ruby warnings

12 rspec —help

**** Filtering/tags ****

In addition to the following options for selecting specific files, groups, or examples, you can select individual examples by appending the line number(s) to the filename:

rspec path/to/a_spec.rb:37:87

You can also pass example ids enclosed in square brackets:

rspec path/to/a_spec.rb[1:5,1:6] # run the 5th and 6th examples/groups defined in the 1st group

--only-failures Filter to just the examples that failed the last time they ran. --next-failure Apply `--only-failures` and abort after one failure. (Equivalent to `--only-failures --fail-fast --order defined`)

13 rspec —help

-P, --pattern PATTERN Load files matching pattern (default: "spec/**/*_spec.rb"). --exclude-pattern PATTERN Load files except those matching pattern. Opposite effect of --pattern. -e, --example STRING Run examples whose full nested names include STRING (may be used more than once) -t, --tag TAG[:VALUE] Run examples with the specified tag, or exclude examples by adding ~ before the tag. - e.g. ~slow - TAG is always converted to a symbol --default-path PATH Set the default path where RSpec looks for examples (can be a path to a file or a directory).

**** Utility ****

-v, --version Display the version. -h, --help You're looking at it.

14 rspec - Getting Ready

2. Next, prepare the directory structure: $ mkdir lib

3. Now run RSpec: $ rspec --init

4 To show what we're testing, we'll change the .rspec file generated for us, replacing progress with doc: - -color --format doc 5. We can now run RSpec on our empty specs directory and verify we have the gem installed: $ rspec spec No examples found. Finished in 0.00004 seconds 0 examples, 0 failures

15 rspec - Getting Ready

RSpec creates a spec directory, a spec_helper.rb file within that directory, and a .rspec file in the current directory with sensible defaults. If you have a problem locating the .rspec file, it's because the file is hidden. A command-line editor such as has no problem opening a hidden file. for example vim .rspec, but using a common dialog box to select a hidden file can be difficult. In OS X, while the Open dialog box is shown, you can press command + shift + . (period) to temporarily show these hidden files.

16 rspec - Getting Ready

RSpec creates a spec directory, a spec_helper.rb file within that directory, and a .rspec file in the current directory with sensible defaults. If you have a problem locating the .rspec file, it's because the file is hidden. A command-line editor such as Vim has no problem opening a hidden file. for example vim .rspec, but using a common dialog box to select a hidden file can be difficult. In OS X, while the Open dialog box is shown, you can press command + shift + . (period) to temporarily show these hidden files.

17 Getting started

Begin with a very simple example that expresses some basic desired behaviour.

# game_spec.rb

RSpec.describe Game do describe "#score" do it "returns 0 for an all gutter game" do game = Game.new 20.times { game.roll(0) } expect(game.score).to eq(0) end end end

18 Getting started

Run the example and watch it fail. Surendras-MacBook-Pro:rspect_test SurendraMac$ rspec spec/game_spec.rb /Users/SurendraMac/rspect_test/spec/ game_spec.rb:3:in `': uninitialized constant Game (NameError) from /usr/local/lib/ruby/gems/2.2.0/gems/rspec- core-3.3.2/lib/rspec/core/configuration.rb: 1327:in `load' from /usr/local/lib/ruby/gems/2.2.0/gems/rspec- core-3.3.2/lib/rspec/core/configuration.rb: 1327:in `block in load_spec_files'

19 Getting started

Now write just enough code to make it pass. require ‘game' RSpec.describe Game do describe "#score" do it "returns 0 for an all gutter game" do game = Game.new 20.times { game.roll(0) } expect(game.score).to eq(0) end end end

20 Getting started

# game.rb class Game def roll(pins) end

def score 0 end end

Surendras-MacBook-Pro:rspect_test SurendraMac$ rspec spec/game_spec.rb Finished in 0.00108 seconds (files took 0.09666 seconds to load) 1 example, 0 failures

21 Getting started

Run the example and bask in the joy that is green.

$rspec spec/game_spec.rb --color --format doc

Game #score returns 0 for an all gutter game

Finished in 0.0008 seconds (files took 0.08722 seconds to load) 1 example, 0 failures

Surendras-MacBook-Pro:rspect_test SurendraMac$

22 rspec-core rspec-core provides the structure for writing executable examples of how your code should behave, and an rspec command with tools to constrain which examples get run and tailor the output. Install gem install rspec # for rspec-core, rspec-expectations, rspec-mocks gem install rspec-core # for rspec-core only rspec --help

23 rspec-core

Want to run against the master branch? You'll need to include the dependent RSpec repos as well. Add the following to your Gemfile:

%w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|

gem lib, :git => "git://github.com/rspec/#{lib}.git", :branch => 'master' end

24 rspec-core :Basic Structure

RSpec uses the words "describe" and "it" so we can express concepts like a conversation: "Describe an order." "It sums the prices of its line items." RSpec.describe Order do it "sums the prices of its line items" do order = Order.new order.add_entry(LineItem.new(:item => Item.new( :price => Money.new(1.11, :USD) )))

…….

25 rspec-core :Basic Structure

.. order.add_entry(LineItem.new(:item => Item.new( :price => Money.new(2.22, :USD), :quantity => 2 ))) expect(order.total).to eq(Money.new(5.55, :USD)) end end

26 rspec-core :Basic Structure

.. :price => Money.new(1.11, :USD) ))) order.add_entry(LineItem.new(:item => Item.new( :price => Money.new(2.22, :USD), :quantity => 2 ))) expect(order.total).to eq(Money.new(5.55, :USD)) end end The describe method creates an ExampleGroup. Within the block passed to describe you can declare examples using the it method. Under the hood, an example group is a class in which the block passed to describe is evaluated. The blocks passed to it are evaluated in the context of an instance of that class.

27 rspec-core : Nested Groups

#declare nested nested groups using the describe or context methods RSpec.describe Order do context "with no items" do it "behaves one way" do # ... end end context "with one item" do it "behaves another way" do # ... end end end

28 rspec-core : Aliases

You can declare example groups using either describe or context. For a top level example group, describe and context are available off of RSpec. For backwards compatibility, they are also available off of the main object and Module unless you disable monkey patching.

You can declare examples within a group using any of it, specify, or example.

29 rspec-core :Shared Examples and Contexts

Declare a shared example group using shared_examples, and then include it in any group using include_examples. RSpec.shared_examples "collections" do |collection_class| it "is empty when first created" do expect(collection_class.new).to be_empty end end RSpec.describe Array do include_examples "collections", Array end RSpec.describe Hash do include_examples "collections", Hash end

30 RSpec Array Matcher

If you're testing arrays a lot, like ActiveRecord's (named) scopes, you should know the following RSpec matcher: =~. It doesn't care about sorting and it gives you all the output you need when the spec fails. Here is an example: describe "array matching" do it "should pass" do [ 1, 2, 3 ].should =~ [ 2, 3, 1 ] end it "should fail" do [ 1, 2, 3 ].should =~ [ 4, 2, 3 ] end end

31 RSpec Array Matcher

32 rspec-core :Shared Examples and Contexts

Nearly anything that can be declared within an example group can be declared within a shared example group. This includes before, after, and around hooks, let declarations,and nested groups/contexts.

You can also use the names shared_context and include_context. These are pretty much the same as shared_examples and include_examples, providing more accurate naming when you share hooks, let declarations, helper methods, etc,but no examples.

33 rspec-core :Metadata rspec-core stores a metadata hash with every example and group, which contains their descriptions, the locations at which they were declared, etc. This hash powers many of rspec-core's features, including output formatters (which access descriptions and locations), and filtering before and after hooks.

Although you probably won't ever need this unless you are writing an extension, you can access it from an example like this: it "does something" do |example| expect(example.metadata[:description]).to eq("does something") end

34 rspec-core :described_class

When a class is passed to describe, you can access it from an example using the described_class method, which is a wrapper for example.metadata[:described_class].

RSpec.describe Widget do example do expect(described_class).to equal(Widget) end end

35 rspec-core :described_class

This is useful in extensions or shared example groups in which the specific class is unknown. Taking the collections shared example group from above, we can clean it up a bit using described_class:

RSpec.shared_examples "collections" do it "is empty when first created" do expect(described_class.new).to be_empty end end

RSpec.describe Array do include_examples "collections" end

RSpec.describe Hash do include_examples "collections" end

36 rspec-core :A Word on Scope

RSpec has two scopes: Example Group: Example groups are defined by a describe or context block, which is eagerly evaluated when the spec file is loaded. The block is evaluated in the context of a subclass of RSpec::Core::ExampleGroup, or a subclass of the parent example group when you're nesting them. Example: Examples -- typically defined by an it block -- and any other blocks with per-example semantics -- such as a before(:example) hook -- are evaluated in the context of an instance of the example group class to which the example belongs. Examples are not executed when the spec file is loaded; instead, RSpec waits to run any examples until all spec files have been loaded, at which point it can apply filtering, randomization, etc.

37 rspec-core :A Word on Scope

To make this more concrete, consider this code snippet:

RSpec.describe "Using an array as a stack" do def build_stack [] end

before(:example) do @stack = build_stack end

it 'is initially empty' do expect(@stack).to be_empty end

38 rspec-core :A Word on Scope

context "after an item has been pushed" do before(:example) do @stack.push :item end

it 'allows the pushed item to be popped' do expect(@stack.pop).to eq(:item) end end end

39 rspec-core :A Word on Scope

Under the covers, this is (roughly) equivalent to: class UsingAnArrayAsAStack < RSpec::Core::ExampleGroup def build_stack [] end

def before_example_1 @stack = build_stack end

def it_is_initially_empty expect(@stack).to be_empty end

40 rspec-core :A Word on Scope

class AfterAnItemHasBeenPushed < self def before_example_2 @stack.push :item end

def it_allows_the_pushed_item_to_be_popped expect(@stack.pop).to eq(:item) end end end

41 rspec-core :A Word on Scope

To run these examples, RSpec would (roughly) do the following: example_1 = UsingAnArrayAsAStack.new example_1.before_example_1 example_1.it_is_initially_empty example_2 = UsingAnArrayAsAStack::AfterAnItemHasBeenPushed.new example_2.before_example_1 example_2.before_example_2 example_2.it_allows_the_pushed_item_to_be_popped

42 rspec-core :A Word on Scope

Store Command Line Options .rspec You can store command line options in a .rspec file in the project's root directory, and the rspec command will read them as though you typed them on the command line.

43 rspec-core :Get Started

Start with a simple example of behavior you expect from your system. Do this before you write any implementation code:

# in spec/calculator_spec.rb RSpec.describe Calculator do describe '#add' do it 'returns the sum of its arguments' do expect(Calculator.new.add(1, 2)).to eq(3) end end end

44 rspec-core :Get Started

Run this with the rspec command, and watch it fail:

$ rspec spec/calculator_spec.rb ./spec/calculator_spec.rb:1: uninitialized constant Calculator Address the failure by defining a skeleton of the Calculator class:

# in lib/calculator.rb class Calculator def add(a, b) end end

45 rspec-core :Get Started

Be sure to require the implementation file in the spec:

# in spec/calculator_spec.rb # - RSpec adds ./lib to the $LOAD_PATH require "calculator" Now run the spec again, and watch the expectation fail:

$ rspec spec/calculator_spec.rb F

Failures:

46 rspec-core :Get Started

Failures:

1) Calculator#add returns the sum of its arguments Failure/Error: expect(Calculator.new.add(1, 2)).to eq(3)

expected: 3 got: nil

(compared using ==) # ./spec/calcalator_spec.rb:6:in `block (3 levels) in '

Finished in 0.00131 seconds (files took 0.10968 seconds to load) 1 example, 1 failure

Failed examples: rspec ./spec/calcalator_spec.rb:5 # Calculator#add returns the sum of its arguments

47 rspec-core :Get Started

Implement the simplest solution, by changing the definition of Calculator#add to: def add(a, b) a + b end Now run the spec again, and watch it pass:

$ rspec spec/calculator_spec.rb . Finished in 0.000315 seconds 1 example, 0 failures

48 rspec-core :Get Started

Use the documentation formatter to see the resulting spec:

$ rspec spec/calculator_spec.rb --format doc Calculator #add returns the sum of its arguments

Finished in 0.000379 seconds 1 example, 0 failures

49 RSpec Best Practices

• RSpec is a great tool in the behavior driven design process of writing human readable specifictions that direct and validate the development of your application. • I've found the following practices helpful in writing elegant and maintainable specifications. • First #describe What You Are Doing

• Begin by using #describe for each of the methods you plan on defining, passing the method’s name as the argument. For class method specs prefix a "." to the name, and for instance level specs prefix a "#". This follows standard Ruby documentation practices and will read well when output by the spec runner.

50 RSpec Best Practices

describe User do

describe '.authenticate' do end

describe '.admins' do end

describe '#admin?' do end

describe '#name' do end end

51 RSpec Best Practices

Then Establish The #context

Next use #context to explain the different scenarios in which the method could be executed. Each #context establishes the state of the world before executing the method. Write one for each execution path through a method.

For example, the following method has two execution paths: class SessionsController < ApplicationController

def create user = User.authenticate :email => params[:email], :password => params[:password] if user.present? session[:user_id] = user.id redirect_to root_path else flash.now[:notice] = 'Invalid email and/or password' render :new end end end

52 RSpec Best Practices

The spec for this method would consists of two contexts: describe '#create' do context 'given valid credentials' do end context 'given invalid credentials' do end end

53 RSpec Best Practices

Note the use of the word "given" in each #context. This communicates the context of receiving input. Another great word to use in a context for describing conditional driven behavior is "when". describe '#destroy' do context 'when logged in' do end

context 'when not logged in' do end end

54 RSpec Best Practices

And Finally Specify The Behavior Strive to have each example specify only one behavior. This will increase the readability of your specs and make failures more obvious and easier to debug. The following is a spec with multiple un-related behaviors in a single example: describe UsersController do describe '#create' do .. it 'creates a new user' do User.count.should == @count + 1 flash[:notice].should be response.should redirect_to(user_path(assigns(:user))) end end end

55 RSpec Best Practices

Break out the expectations into separate examples for a more clear definition of the different behaviors. describe UsersController do describe '#create' do ... it 'creates a new user' do User.count.should == @count + 1 end it 'sets a flash message' do flash[:notice].should be end it "redirects to the new user's profile" do response.should redirect_to(user_path(assigns(:user))) end end end

56 RSpec Best Practices

Tips For Better Examples Lose The Should Don't begin example names with the word "should". It is redundant and results in hard to read spec output. Instead write examples by starting with a present tense verb that describes the behavior. it 'creates a new user' do end it 'sets a flash message' do end it 'redirects to the home page' do end

57 RSpec Best Practices

Tips For Better Examples Lose The Should

.. it 'finds published posts' do end it 'enqueues a job' do end it 'raises an error' do end Don't hesitate to use words like "the" or "a" or "an" in your examples when they improve readability.

58 RSpec Best Practices

Use The Right Matcher RSpec comes with a lot of useful matchers to help your specs read more like natural language. When you feel there is a cleaner way ... there usually is.

Here are some common matcher refactorings to help improve readability. # before: double negative object.should_not be_nil # after: without the double negative object.should be # before: "lambda" is too low level lambda { model.save! }.should raise_error(ActiveRecord::RecordNotFound) # after: for a more natural expectation replace "lambda" and

59 RSpec Best Practices

# after: for a more natural expectation replace "lambda" and "should" with "expect" and "to" expect { model.save! }.to raise_error(ActiveRecord::RecordNotFound) # the negation is also available as "to_not" expect { model.save! }.to_not raise_error(ActiveRecord::RecordNotFound) # before: straight comparison collection.size.should == 4 # after: a higher level size expectation collection.should have(4).items

60 RSpec Best Practices

Prefer Explicitness

#it, #its and #specify may cut down on the amount of typing but they sacrifice readability. Using these methods requires you to read the body of the example in order to determine what its specifying. Use these sparingly if at all.

Let's compare the output from the documentation formatter of the following spec that uses these more concise example methods. describe PostsController do

describe '#new' do context 'when not logged in' do ...

61 RSpec Best Practices

context 'when not logged in' do ...

subject do response end

it do should redirect_to(sign_in_path) end

its :body do should match(/sign in/i) end end end end

62 RSpec Best Practices

$ rspec spec/controllers/posts_controller_spec.rb -- format documentation

PostsController #new when not logged in should redirect to "/sign_in" should match /sign in/i Running this spec results in blunt, code-like output with redundancy from using the word "should" multiple times.

63 RSpec Best Practices

Here is the same spec using more verbose, explicit examples: describe PostsController do

describe '#new' do context 'when not logged in' do ...

it 'redirects to the sign in page' do response.should redirect_to(sign_in_path) end

it 'displays a message to sign in' do response.body.should match(/sign in/i) end end end end

64 RSpec Best Practices

$ rspec spec/controllers/posts_controller_spec.rb --format documentation

PostsController #new when not logged in redirects to the sign in page displays a message to sign in This version results in a very clear, readable specification.

65 RSpec Best Practices

Run Specs To Confirm Readability

Always run your specs with the "--format" option set to "documentation" (in RSpec 1.x the --format options are "nested" and "specdoc")

$ rspec spec/controllers/users_controller_spec.rb --format documentation

UsersController #create creates a new user sets a flash message redirects to the new user's profile #show finds the given user displays its profile #show.json returns the given user as JSON #destroy deletes the given user sets a flash message redirects to the home page

66 RSpec Best Practices

Continue to rename your examples until this output reads like clear conversation.

Formatting

Use "do..end" style multiline blocks for all blocks, even for one-line examples. Further improve readability and delineate behavior with a single blank line between all #describe blocks and at the beginning and end of the top level #describe.

Before: describe PostsController do describe '#new' do context 'when not logged in' do ... subject { response } it { should redirect_to(sign_in_path) } its(:body) { should match(/sign in/i) } end end end

67 RSpec Best Practices

And after: describe PostsController do

describe '#new' do context 'when not logged in' do ...

it 'redirects to the sign in page' do response.should redirect_to(sign_in_path) end

it 'displays a message to sign in' do response.body.should match(/sign in/i) end end end end

68 RSpec Best Practices

A consistent formatting style is hard to achieve with a team of developers but the time saved from having to learn to visually parse each teammate's style makes it worthwhile.

Conclusion

As you can see, all these practices revolve around writing clear specifications readable by all developers. The ideal is to run all specs to not only pass but to have their output completely define your application. Every little step towards that goal helps.

69 Pry Introduction

• Pry is an interactive shell for the Ruby programming language. It is notable for its -inspired ability to start a REPL within a running program. • This lets programmers debug and modify the current state of a system. • Pry exposes most of its introspective capabilities using a filesystem metaphor. For example, it has a `cd` command to start interacting with a particular object, and uses `ls` to list methods and variables. • It is possible to start Pry at any point inside a running program. • Due to the reflective nature of Ruby, this lets the inspect the program, change its current state, or correct the without restarting the process.

70 Pry Introduction

• The main competitor to Pry is IRB, a standalone interactive shell that is packaged with releases of the Ruby programming language. • There are a reasonable number of third-party plugins that add features to make IRB behave more like Pry, packaged as irbtools.

71 Debugging With Pry

• Pry is a REPL (Read Eval Print Loop) that was written as a better alternative to IRB. • It comes with syntax highlighting, code indentation that actually works and several other features that make it easier to debug code. • I stumbled upon Pry when looking for an alternative to both IRB and the way I was debugging my code (placing puts all over the place, I think it’s called “Puts Driven Development”). • Pry is primarily meant to be used as a REPL. There are a lot of things that make Pry so much more pleasant to use than IRB. One of the things almost any Ruby programmer will notice when using IRB is that its indentation support is a bit clunky.

72 Debugging With Pry

• Surendras-MacBook-Pro:~ SurendraMac$ irb • irb(main):001:0> class User • irb(main):002:1> def greet • irb(main):003:2> puts "Hello world” • irb(main):005:2> end • irb(main):006:1> end • => :greet • irb(main):007:0>

73 Debugging With Pry

• Pry handles this just fine, whether you’re trying to indent a class or a hash containing an array containing a proc and so on. • Pry does this by resetting the terminal output every time a new line is entered. • The downside of this approach is that it only works on terminals that understand ANSI escape codes. • In Pry the above example works like it should do:

74 Debugging With Pry

• SurendraMac$ pry • [1] pry(main)> class User • [1] pry(main)* def greet • [1] pry(main)* puts "hello world" • [1] pry(main)* end • [1] pry(main)* end • => :greet • [2] pry(main)> quit

75 Debugging With Pry

• Besides indentation Pry does a lot more. • A feature that I think is very cool is the ability to show documentation and source code of methods right in your REPL (sadly this feature doesn’t work with classes or modules at the time of writing). • This means that you no longer have to use the • ri command to search documentation for methods. • You also don’t need to install the RDoc documentation as Pry pulls it directly from the source code. • Showing the source code of a method or its documentation can be done by using the show-method and show-doc command.

76 Debugging With Pry

• For example, invoking show-method pry in a Pry session would give you the following output: • Surendras-MacBook-Pro:~ SurendraMac$ pry • [1] pry(main)> show-method pry

• From: /usr/local/lib/ruby/gems/2.2.0/gems/pry-0.10.3/lib/pry/core_extensions.rb @ line 41: • Owner: Object • Visibility: public • Number of lines: 7

• def pry(object=nil, hash={}) • if object.nil? || Hash === object • Pry.start(self, object || {}) • else • Pry.start(object, hash) • end • end • [2] pry(main)>

77 show-doc pry

• [2] pry(main)> show-doc pry • From: /usr/local/lib/ruby/gems/2.2.0/gems/pry-0.10.3/lib/pry/core_extensions.rb @ line 23: • Owner: Object • Visibility: public • Signature: pry(object=?, hash=?) • Number of lines: 18

• Start a Pry REPL on self.

• If `self` is a Binding then that will be used to evaluate expressions; • otherwise a new binding will be created.

• param [Object] object the object or binding to pry • (__deprecated__, use `object.pry`) • param [Hash] hash the options hash

78 show-doc pry

• example With a binding • binding.pry • example On any object • "dummy".pry • example With options • def my_method • binding.pry :quiet => true • end • my_method() • @see Pry.start • [3] pry(main)>

79 show-doc pry

• You can also run these commands for code that was written in C. This requires you to install the gem pry-doc (gem install pry-doc). Do note that this only works for core C code, currently Pry does not support this for third party extensions. • Another very cool feature is that Pry can be used as a debugging tool for your code without having to manually jump into a session. • By loading Pry, which can be done by writing require "pry" or by using the option -r pry when invoking Ruby you gain access to everything Pry has to offer. • The most useful tool is binding.pry. This method starts a Pry session and pauses the script.

80 show-doc pry

• Surendras-MacBook-Pro:~ SurendraMac$ sudo gem install pry-doc • Password: • Fetching: -0.8.7.6.gem (100%) • Successfully installed yard-0.8.7.6 • Fetching: pry-doc-0.8.0.gem (100%) • Successfully installed pry-doc-0.8.0 • Parsing documentation for yard-0.8.7.6 • Installing ri documentation for yard-0.8.7.6 • Parsing documentation for pry-doc-0.8.0 • Installing ri documentation for pry-doc-0.8.0 • Done installing documentation for yard, pry-doc after 4 seconds • 2 gems installed • Surendras-MacBook-Pro:~ SurendraMac$

81 show-doc pry

• Lets say you have the following script and want to see the values of the variables: • language = 'Ruby' • number = 10 • # Do something awesome with the above variables. • The typical approach would be to insert a puts statement above the comment followed by an exit statement. • Pry in a way can do a similar thing, it just makes it a lot more awesome.

82 show-doc pry

• If you modify the script as following you can truly debug your code like a boss: • language = 'Ruby' • number = 10 • binding.pry • # Do something awesome with the above variables.

83 show-doc pry

• If you now run the script by calling ruby -r pry file.rb you get a fancy Pry session:

• $ ruby -r pry file.rb

84 show-doc pry

• A nice thing about starting Pry this way is that it starts in the context of the call to binding.pry meaning you get access to data such as the local variables. • These can be displayed by calling ls or by simply typing their name. • $ ruby -r pry file.rb

• From: file.rb @ line 4 in Object#N/A:

• 1: language = 'Ruby' • 2: number = 10 • 3: • => 4: binding.pry • 5: • 6: # Do something awesome with the above variables.

85 show-doc pry

• [1] pry(main)> ls • self methods: include private public to_s • locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_ language number • [2] pry(main)> number • => 10 • [3] pry(main)>

86 show-doc pry

• Moving out of the “breakpoint” (or moving to the next one if you have multiple ones defined) can be done by hitting ^D (Ctrl+D usually). • For example, long output is piped to Less. • This can be quite useful if you’re trying to display a big hash using pp. • The full list of features can be found on the Pry website as well as by invoking the help command inside a Pry session.

87 Thanks

Surendra R Panpaliya, International Corporate Trainer and Consultant GKTCS Innovations Pvt. Ltd, Near Warje Flyover Bridge, Warje-Malwadi, Pune-411058 Email: Surendra R Panpaliya, Founder and Director, GKTCS Innovations Pvt. Ltd, 11, 4th Floor,Sneh Deep, Near Warje Flyover Bridge, Warje-Malwadi, Pune-411058 Email: [email protected] Web:www.gktcs.com

88