_ruby
A Solution
A common utility most programmers in the Ruby community are familiar with is the Interactive Ruby Shell (IRB). This is a REPL that comes with Ruby that enables you to send and receive Ruby commands. While this is useful, it leaves much to be desired, and an excellent alternative is the Pry gem. It not only lets you execute Ruby commands to test your input and output, but it also allows you to pry inside your code and interact with it while running.
You can use the Pry gem to help you construct your scripts (like a simple REPL), but you can also use it as a helpful debugging tool.
To install Pry, you can either add it to your Gemfile like so:
source 'https://rubygems.org'
gem 'pry'
And then run bundle install
. Or, you can install it from the commandline with gem install pry
.
Example
Here I will walk you through a real life example of how I recently used Pry to debug an issue in one of my test scripts.
To give you some context, here is a simple example of some of my test code.
include SiteCheckers
@driver.get("http://www.mysite.com")
everything_ready.should == true
# ... other code that tests other aspects of the site
We include a module of helpful methods for use in our scripts by including SiteCheckers
. We assume that the driver
is already setup and our tests are using Rspec. And everything_ready
is a method mixed in from SiteCheckers
.
It dynamically checks the page to ensure that we are on the right one and that it is rendered correctly.
With this context in mind, here is the specific example I was working with recently:
include SiteCheckers
include FeatureItems
@driver.get("http://www.mysite.com/some-feature")
everything_ready.should == true
# ... other code that tests other aspects of the feature
Uh oh. When I ran this test, I got an error.
RSpec::Expectations::ExpectationNotMetError: expected: true
got: nil (using ==)
That is real funny, because when I looked at the code for this method, nothing should have caused it to return nil:
module SiteCheckers
def everything_ready
# redacted code checks to ensure the page is rendered
# and raises error if there is an issue
true
end
# ... other helper methods defined here
end
So, what did I do to solve this issue? I used Pry to debug the error and discover my real issue!
To initialize Pry in your code, you have to require the gem, and then place binding.pry
wherever you want
execution of your code to pause, so you can jump in and look around for yourself. For example:
require 'pry'
include SiteCheckers
include FeatureItems
@driver.get("http://www.mysite.com/some-feature")
# With this, we jump right in before the error in the script
binding.pry
everything_ready.should == true
And when we run the test, we will get a prompt like this:
Frame number: 0/1
From: /home/bgoad/es-pry/pry_test.rb @ line 38 :
33: include FeatureItems
34:
35: @driver.get("http://www.mysite.com/some-feature")
36:
37: # We'll jump right in before the error in the script
=> 38: binding.pry
39:
40: everything_ready.should == true
[1] pry(main)>
Which means that we have jumped right into the context of our code at the spot where the rocket-arrow is pointing (binding.pry). From here, we can use Unix-like commands to explore our code inside the REPL prompt.
ls
will provide a list of Classes, Modules, methods, instance variables, and private variables available in this
context. This is a great command to learn more about what is available to you at this point. Two other helpful
commands are cd
and help
. You can use cd
to change the context to inside an object, class or module. And
help
provides you with a quick access of commands available to Pry.
Browsing through these commands will allow you to discover other neat tricks you can do with Pry. You can also execute any Ruby code you want from Pry's prompt.
Let's take a closer look at the everything_ready
method:
pry(main)> everything_ready
=> nil
Hmm, this method is not returning true
as we expect in the test.
Let's see if Pry can help
us. One such command available from Pry is show-method
, which will print out the
code of the method you pass it. Let's do that:
pry(main)> show-method everything_ready
From: pry_test.rb @ line 21:
Owner: FeatureItems
Visibility: public
Number of lines: 6
def everything_ready
super
if @driver.find_element(:css => "#modal_dialog").displayed?
@driver.find_element(:css => "#modal_dialog_close").click
end
end
Oh, look! I was expecting the #everything_ready method from SiteCheckers, but according to the Owner field from
Pry's output, we are overriding the method inside the FeatureItems
module in order to add some extra functionality
(presumably, closing a modal if it is shown on the page).
The reason the method is not returning true is because the last line, which is what will be returned, does not
result in true. Instead, when an if..end
statement is executed, it always returns a nil. We can test this in Pry:
pry(main)> if true
pry(main)* puts "yay"
pry(main)* end
yay
=> nil
pry(main)> if false
pry(main)* puts "boo"
pry(main)* end
=> nil
The rocket-arrows after the command we executed indicate the result of the command, which is nil. So, I need to
update the overridden #everything_ready method to return the result of super
instead of the if..end
statement.
What's really cool is that I can do so right inside of Pry using the edit
command! Typing edit everything_ready
will pop me inside the FeatureItems#everything_ready method using my favorite terminal editor. Here, I can make the
updates to my code:
def everything_ready
result = super
if @driver.find_element(:css => "#modal_dialog").displayed?
@driver.find_element(:css => "#modal_dialog_close").click
end
result
end
Now I save and quit the editor. Once I am back in the Pry prompt, it reloads the file I edited and allows me to try my code again.
Frame number: 0/1
From: /home/bgoad/es-pry/pry_test.rb @ line 38 :
33: include FeatureItems
34:
35: @driver.get("http://www.mysite.com/some-feature")
36:
37: # We'll jump right in before the error in the script
=> 38: binding.pry
39:
40: everything_ready.should == true
pry(main)> everything_ready.should == true
=> true
To exit out of Pry and let my test finish running, I simply type exit
. And now both tests succeed! Don't forget
to remove the binding.pry
line from inside the test, or else Pry will start right back up again inside the code.
Summary
Nice! I was able to use Pry to jump into my code right before a failure in my test, have a look around in the environment, debug why my code was failing, and make a quick fix. And I was finally able to verify that my fix was working by running the command in my test again. Pry is pretty darn sweet!
There are plenty of other uses for pry, this tip just scratches the surface. In fact, the Pry community includes several useful plugins to enhance your Pry experience. Some of the most popular ones include pry-rescue (deposits you into Pry at the point and context of a failure or error in your code), pry-stack_explorer (enables you to travel up and down the stack), and pry-debug (includes actual debugger functionality like step and continue).
I encourage you to take a look around the community and see how Pry can make your construction and debugging of tests so much more efficient. And not only that, you'll also begin to learn why your code works in certain ways and how it is structured. Making you a much better test automator and coder as a result.
Happy Prying!
About The Author
You can follow Brian on Twitter at @bbbco, and check out his testing blog here.