Tag Archives: Testing Your Ruby Code With Guard

Testing Your Ruby Code With Guard, RSpec & Pry

My recent work has been on a cloud based Ruby project for the BBC News, upcoming 2014 elections. It requires fast I/O, scalability and needs to be well tested. The “be well tested” requirement, is what I want to focus on in this tutorial.

Introduction

This project utilizes a few different Amazon services such as:

  • SQS (Simple Queue Service)
  • DynamoDB (key/value store)
  • S3 (Simple Storage Service)

We need to be able to write tests that are fast and give us instant feedback on problems with our code.

Although, we won’t be using Amazon services in this tutorial, I mention them because for us to have tests that are fast, it requires us to fake these external objects (for example, we shouldn’t need a network connection to run our tests, because that dependency can result in slow running tests).

Alongside tech lead Robert Kenny (who is very well versed in writing TDD (test-driven development) based Ruby applications) we’ve been utilizing different tools that have made this process and our programming work a lot easier.

I want to share some information about these tools with you.

The tools I’ll be covering are:

  • RSpec (testing framework)
  • Guard (task runner)
  • Pry (REPL and debugging)

What Do I Need to Know Upfront?

I’m going to make an assumption that you are familiar with Ruby code and the Ruby eco-system. For example, I shouldn’t need to explain to you what ‘gems’ are or how certain Ruby syntax/concepts work.

If you’re unsure, then before you move, on I would recommend reading one of my other posts on Ruby to get yourself up to speed.

Guard

You might not be familiar with Guard, but in essence it’s a command line tool that utilizes Ruby to handle different events.

For example, Guard can notify you whenever specific files have been edited and you can carry out some action based on the type of file or event that was fired.

This is known as a ‘task runner’, you may have heard the phrase before, as they are getting a lot of usage in the front-end/client-side world at the moment (Grunt and Gulpare two popular examples).

The reason we’ll be using Guard is because it helps make the feedback loop (when doing TDD) a lot tighter. It allows us to edit our test files, see a failing test, update and save our code and immediately see if it passes or fails (depending on what we wrote).

You could use something like Grunt or Gulp instead, but we prefer to use those types of task runners for handling front-end/client-side stuff. For back-end/server-side code, we use Rake and Guard.

RSpec

RSpec, if you weren’t already aware, is a testing tool for the Ruby programming language.

You run your tests (using RSpec) via the command line and I’ll demonstrate how you can make this process easier via the use of Ruby’s build program, Rake.

Pry

Lastly, we’ll be using another Ruby gem called Pry which is an extremely powerful Ruby debugging tool which injects itself into your application, while it is running, to allow you to inspect your code and figure out why something isn’t working.

TDD (Test-Driven Development)

Although not necessary for demonstrating the use of RSpec and Guard, it’s worth noting that I fully endorse the use of TDD as a means to ensure every line of code you write has a purpose and has been designed in a testable and reliable manner.

I’ll be detailing how we would do TDD with a simple application, so at least you’ll get a feel for how the process works.

Creating an Example Project

I’ve created a basic example on GitHub to save you from having to type everything out yourself. Feel free to download the code.

Let’s now go ahead and review this project, step-by-step.

Primary Files

There are three primary files required for our example application to work, these are:

  1. Gemfile
  2. Guardfile
  3. Rakefile

We’ll go over the content of each file shortly, but the first thing we need to do is get our directory structure in place.

Directory Structure

For our example project, we’ll need two folders created:

  • lib (this will hold our application code)
  • spec (this will hold our test code)

This isn’t a requirement for your application, you can easily tweak the code within our other files to work with whatever structure suits you.

Installation

Open up your terminal and run the following command:

1
gem install bundler

Bundler is a tool that makes installing other gems easier.

Once you’ve run that command, create the above three files (GemfileGuardfileand Rakefile).

Gemfile

The Gemfile is responsible for defining a list of dependencies for our application.

Here is what it looks like:

1
2
3
4
5
6
7
8
9
gem 'rspec'
group :development do
  gem 'guard'
  gem 'guard-rspec'
  gem 'pry'
end

Once this file is saved, run the command bundle install.

This will install all of our gems for us (including those gems specified within thedevelopment group).

The purpose of the development group (which is a bundler specific feature) is so when you deploy your application, you can tell your production environment to install only the gems that are required for your application to function properly.

So for example, all the gems inside the development group, aren’t required for the application to function properly. They are used solely to aid us while we’re developing and testing our code.

To install the appropriate gems on your production server, you would need to run something like:

1
bundle install --without development

Rakefile

The Rakefile will allow us to run our RSpec tests from the command line.

Here is what it looks like:

1
2
3
4
5
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new do |task|
  task.rspec_opts = ['--color', '--format', 'doc']
end

Note: you don’t need Guard to be able to run your RSpec tests. We use Guard to make it easier to do TDD.

When you install RSpec, it gives you access to a built in Rake task and that’s what we’re using here.

We create a new instance of RakeTask which by default creates a task called specthat will look for a folder called spec and will run all the test files within that folder, using the configuration options we’ve defined.

In this instance, we want our shell output to have color and we want to format the output to the doc style (you could change the format to be nested as an example).

You can configure the Rake task to work any way you want and to look into different directories, if that’s what you have. But the default settings work great for our application and so that’s what we’ll be using.

Now if I want to run the tests in my example GitHub repository, then I need to open up my terminal and run the command:

1
rake spec

This gives us the following output:

1
2
3
4
5
6
7
8
rake spec
/bin/ruby -S rspec ./spec/example_spec.rb --color --format doc
RSpecGreeter
  RSpecGreeter#greet()
Finished in 0.0006 seconds
1 example, 0 failures

As you can see there are zero failures. That’s because although we have no application code written, we also don’t have any test code written yet either.

Guardfile

The contents of this file tells Guard what to do when we run the guard command:

01
02
03
04
05
06
07
08
09
10
11
guard 'rspec' do
  # watch /lib/ files
  watch(%r{^lib/(.+).rb$}) do |m|
    "spec/#{m[1]}_spec.rb"
  end
# watch /spec/ files
  watch(%r{^spec/(.+).rb$}) do |m|
    "spec/#{m[1]}.rb"
  end
end

You’ll have noticed within our Gemfile we specified the gem: guard-rspec. We need that gem to allow Guard to understand how to handle changes to RSpec related files.

If we look again at the content, we can see that if we ran guard rspec then Guard would watch the specified files and execute the specified commands once any changes to those files had occurred.

Note: because we only have one guard task, rspec, then that is run by default if we ran the command guard.

You can see Guard provides us with a watch function which we pass a Regular Expression to allow us to define what files we’re interested in Guard watching.

In this instance, we’re telling Guard to watch all the files within our lib and specfolders and if any changes occur to any of those files then to execute the test files within our spec folder to make sure no changes we made broke our tests (and subsequently didn’t break our code).

If you have all the files downloaded from the GitHub repo then you can try out the command for yourself.

Run guard and then save one of the files to see it run the tests.

Test Code

Before we start looking at some test and application code, let me explain what our application is going to do. Our application is a single class that will return a greeting message to whoever is running the code.

Our requirements are purposely simplified, as it’ll make the process we’re about to undertake easier to understand.

Let’s now take a look at an example specification (for example, our test file) which will describe our requirements. After that, we’ll start to step through the code defined in the specification and see how we can use TDD to aid us in writing our application.

Our First Test

We’re going to create a file titled example_spec.rb. The purpose of this file is to become our specification file (in other words, this is going to be our test code and will represent the expected functionality).

The reason we write our test code before writing our actual application code is because it ultimately means that any application code we do produce will exist because it was actually used.

That’s an important point I’m making and so let me take a moment to clarify it in more detail.

Writing Test Code Before Application Code

Typically, if you write your application code first (so you’re not doing TDD), then you will find yourself writing code that at some point in the future is over engineered and potentially obsolete. Through the process of refactoring or changing requirements, you may find that some functions will fail to ever be called.

This is why TDD is considered the better practice and the preferred development method to use, because every line of code you produce will has been produced for a reason: to get a failing specification (your actual business requirement) to pass. That’s a very powerful thing to keep in mind.

Here is our test code:

1
2
3
4
5
6
7
8
9
require 'spec_helper'
describe 'RSpecGreeter' do
  it 'RSpecGreeter#greet()' do
    greeter  = RSpecGreeter.new         # Given
    greeting = greeter.greet            # When
    greeting.should eq('Hello RSpec!'# Then
  end
end

You may notice the code comments at the end of each line:

  • Given
  • When
  • Then

These are a form of BDD (Behaviour-Driven Development) terminology. I included them for readers who are more familiar with BDD (Behavior-Driven Development) and who were interested in how they can equate these statements with TDD.

The first thing we do inside this file is load spec_helper.rb (which is found in the same directory as our spec file). We’ll come back and look at the contents of that file in a moment.

Next we have two code blocks that are specific to RSpec:

  • describe 'x' do
  • it 'y' do

The first describe block should adequately describe the specific class/module we’re working on and providing tests for. You could very well have multiple describe blocks within a single specification file.

There are many different theories on how to use describe and it description blocks. I personally prefer simplicity and so I’ll use the identifiers for the Class/Modules/Methods that we’ll be testing. But you often find some people who prefer to use full sentences for their descriptions. Neither is right or wrong, just personal preference.

The it block is different and should always be placed inside a describe block. It should explain how we want our application to work.

Again, you could use a normal sentence to describe the requirements, but I’ve found that sometimes doing so can cause the descriptions to be too explicit, when they should really be more implicit. Being less explicit reduces the chances of changes to your functionality, causing your description to become outdated (having to update your description every time minor functionality changes occur is more of a burden than a help). By using the identifier of the method we’re testing (for example, the name of the method we’re executing) we can avoid that issue.

The contents of the it block is the code we’re going to test.

In the above example, we create a new instance of the class RSpecGreeter (which doesn’t exist yet). We send the message greet (which also doesn’t exist yet) to the instantiated object created (note: these two lines are standard Ruby code at this point).

Finally, we tell the testing framework that we expect the outcome of calling the greet method to be the text “Hello RSpec!“, by using the RSpec syntax: eq(something).

Notice how the syntax allows it to be easily read (even by a non-technical person). These are known as assertions.

There’s a lot of different RSpec assertions and we won’t go into the details, but feel free to review the documentation to see all the features RSpec provides.

Creating a Helper for Our Test

There is a certain amount of boilerplate required for our tests to run. In this project, we only have one specification file but in a real project you’re likely to have dozens (depending on the size of your application).

To help us reduce the boilerplate code, we’ll place it inside of a special helper file that we’ll load from our specification files. This file will be titled spec_helper.rb.

This file will do a couple of things:

  • tell Ruby where our main application code is located
  • load our application code (for the tests to run against)
  • load the pry gem (helps us debug our code; if we need to).

Here is the code:

1
2
3
4
$ << File.join(File.dirname(FILE), '..', 'lib')
require 'pry'
require 'example'

Note: the first line may look a bit cryptic, so let me explain how it works. Here we’re saying we want to add the /lib/ folder to Ruby’s $LOAD_PATH system variable. Whenever Ruby evaluates require 'some_file' it has a list of directories it will try and locate that file. In this instance, we’re making sure that if we have the code require 'example' that Ruby will be able to locate it because it’ll check our /lib/ directory and there, it’ll find the file specified. This is a clever trick you will see used in a lot of Ruby gems, but it can be quite confusing if you’ve never seen it before.

Application Code

Our application code is going to be inside a file titled example.rb.

Before we begin writing any application code, remember that we’re doing this project TDD. So we’re going to let the tests in our specification file guide us on what to do first.

Let’s begin by assuming you’re using guard to run your tests (so every time we make a change to example.rb, Guard will notice the change and proceed to runexample_spec.rb to make sure our tests pass).

For us to do TDD properly, our example.rb file will be empty and so if we open the file and ‘save’ it in its current state, then Guard will run and we’ll discover (unsurprisingly) that our test will fail:

01
02
03
04
05
06
07
08
09
10
11
12
13
Failures:
  1) RSpecGreeter RSpecGreeter#greet()
     Failure/Error: greeter  = RSpecGreeter.new         # Given
     NameError:
       uninitialized constant RSpecGreeter
     # ./spec/example_spec.rb:5:in</code>block (2 levels) in '
 
Finished in 0.00059 seconds
1 example, 1 failure
 
Failed examples:
 
rspec ./spec/example_spec.rb:4 # RSpecGreeter RSpecGreeter#greet()

Now before we go any further, let me clarify again that TDD is based on the premise that every line of code has a reason to exist, so don’t start racing ahead and writing out more code than you need. Only write the minimum amount of code required for the test to pass. Even if the code is ugly or doesn’t fulfill the full functionality.

The point of TDD is to have a tight feedback loop, also known as ‘red, green, refactor’). What this means in practice is:

  • write a failing test
  • write the least amount of code to get it to pass
  • refactor the code

You’ll see in a moment that because our requirements are so simple, there is no need for us to refactor. But in a real project with much more complex requirements, you’ll likely need to take the third step and refactor the code you entered, to get the test to pass.

Coming back to our failing test, as you can see in the error, there is no RSpecGreeterclass defined. Let’s fix that and add the following code and save the file so our tests run:

1
2
3
class RSpecGreeter
  # code will eventually go here
end

This will result in the following error:

1
2
3
4
5
6
7
8
9
Failures:
  1) RSpecGreeter RSpecGreeter#greet()
     Failure/Error: greeter  = greeter.greet            # When
     NoMethodError:
       undefined methodgreet' for #
     # ./spec/example_spec.rb:6:in `block (2 levels) in '
Finished in 0.00036 seconds
1 example, 1 failure

Now we can see that this error is telling us the method greet doesn’t exist, so let’s add it and then again save our file to run our tests:

1
2
3
4
5
class RSpecGreeter
  def greet
    # code will eventually go here
  end
end

OK, we’re almost there. The error we get now is:

01
02
03
04
05
06
07
08
09
10
11
12
Failures:
  1) RSpecGreeter RSpecGreeter#greet()
     Failure/Error: greeter  = greeting.should eq('Hello RSpec!'# Then
       expected: "Hello RSpec!"
            got: nil
       (compared using ==)
     # ./spec/example_spec.rb:7:in `block (2 levels) in '
Finished in 0.00067 seconds
1 example, 1 failure

RSpec is telling us that it was expecting to see Hello RSpec! but instead it got nil(because we defined the greet method but didn’t actually define anything inside the method and so it returns nil).

We’ll add the remaining piece of code that would get our test to pass:

1
2
3
4
5
class RSpecGreeter
  def greet
    "Hello RSpec!"
  end
end

There we have it, a passing test:

1
2
Finished in 0.00061 seconds
1 example, 0 failures

We’re done here. Our test is written and the code is passing.

Conclusion

So far we’ve applied a Test-Driven Development process to building our application, alongside utilizing the popular RSpec testing framework.

We’re going to leave it here for now. Come back and join us for part two where we’ll look at more RSpec specific features as well as using the Pry gem to help you debug and write your code.

Testing Your Ruby Code With Guard, RSpec & Pry: Part 2

So far, we’ve applied a Test-Driven Development process to building our application, alongside utilizing the popular RSpec testing framework. From here, we’re going to investigate some other RSpec features as well as look into using the Pry gem to help you debug and write your code.

Other RSpec Features

Let’s take a moment to review some other RSpec features that we’ve not needed in this simple example application, but you may find useful working on your own project.

Pending Statement

Imagine you write a test but you’re interrupted, or you need to leave for a meeting and haven’t yet completed the code required to get the test to pass.

You could delete the test and re-write it later when you’re able to come back to your work. Or alternatively you could just comment the code out, but that’s pretty ugly and definitely no good when using a version control system.

The best thing to do in this situation is to define our test as ‘pending’ so whenever the tests are run, the test framework will ignore the test. To do this you need to use thepending keyword:

1
2
3
4
5
describe "some method" do
  it "should do something"
    pending
  end
end

Set-Up and Tear-Down

All good testing frameworks allow you to execute code before and after each test is run. RSpec is no different.

It provides us before and after methods which allows us to set-up a specific state for our test to run, and to then clean up that state after the test has run (this is so the state doesn’t leak and effect the outcome of subsequent tests).

01
02
03
04
05
06
07
08
09
10
11
12
13
describe "some method" do
  before(:each) do
    # some set-up code
  end
  after(:each) do
    # some tear-down code
  end
  it "should do something"
    pending
  end
end

Context Blocks

We’ve already seen the describe block; but there is another block which is functionally equivalent called context. You can use it every where you would use describe.

The difference between them is subtle but important: context allows us to define a state for our test. Not explicitly though (we don’t actually set the state by defining acontext block – it instead is for readability purposes so the intent of the following code is clearer).

Here is an example:

01
02
03
04
05
06
07
08
09
10
11
12
13
describe "Some method" do
  context "block provided" do
    it "yields to block" do
      pending
    end
  end
  context "no block provided" do
    it "calls a fallback method" do
      pending
    end
  end
end

Stubs

We can use the stub method to create a fake version of an existing object and to have it return a pre-determined value.

This is useful in preventing our tests from touching live service APIs, and guiding our tests by giving predictable results from certain calls.

Imagine we have a class called Person and that this class has a speak method. We want to test that method works how we expect it to. To do this we’ll stub the speakmethod using the following code:

01
02
03
04
05
06
07
08
09
10
describe Person do
  it "speak()" do
    bob = stub()
    bob.stub(:speak).and_return('hello')
    Person.any_instance.stub(:initialize).and_return(bob)
    
    instance = Person.new
    expect(instance.speak).to eq('hello')
  end
end

In this example, we say that ‘any instance’ of the Person class should have itsinitialize method stubbed so it returns the object bob.

You’ll notice that bob is itself a stub which is set-up so that any time code tries to execute the speak method it will return “hello”.

We then proceed to create a new Person instance and pass the call ofinstance.speak into RSpec’s expect syntax.

We tell RSpec that we’re expecting that call to result in the String “hello”.

Consecutive Return Values

In the previous examples we’ve use the RSpec feature and_return to indicate what our stub should return when it’s called.

We can indicate a different return value each time the stub is called by specifying multiple arguments to the and_return method:

1
2
3
4
5
6
obj = stub()
obj.stub(:foo).and_return(1, 2, 3)
expect(obj.foo()).to eq(1)
expect(obj.foo()).to eq(2)
expect(obj.foo()).to eq(3)

Mocks

Mocks are similar to Stubs in that we are creating fake versions of our objects but instead of returning a pre-defined value we’re more specifically guiding the routes our objects must take for the test to be valid.

To do that we use the mock method:

01
02
03
04
05
06
07
08
09
10
11
describe Obj do
  it "testing()" do
    bob = mock()
    bob.should_receive(:testing).with('content')
    Obj.any_instance.stub(:initialize).and_return(bob)
    instance = Obj.new
    instance.testing('some value')
  end
end

In the above example we create a new Object instance and then call its testingmethod.

Behind the scenes of that code we expect the testing method to be called with the value 'content'. If it isn’t called with that value (which in the above example it’s not) then we know that some piece of our code hasn’t functioned properly.

Subject Block

The subject keyword can be used in a couple of different ways. All of which are designed to reduce code duplication.

You can use it implicitly (notice our it block doesn’t reference subject at all):

1
2
3
4
5
6
describe Array do
  describe "with 3 items" do
    subject { [1,2,3] }
    it { should_not be_empty }
  end
end

You can use it explicitly (notice our it block refers to subject directly):

1
2
3
4
5
6
7
8
9
describe MyClass do
  describe "initialization" do
    subject { MyClass }
    it "creates a new instance" do
      instance = subject.new
      expect(instance).to be_a(MyClass)
    end
  end
end

Rather than constantly referencing a subject within your code and passing in different values for instantiation, for example:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
describe "Foo" do
  context "A" do
    it "Bar" do
      baz = Baz.new('a')
      expect(baz.type).to eq('a')
    end
  end
  context "B" do
    it "Bar" do
      baz = Baz.new('b')
      expect(baz.type).to eq('b')
    end
  end
  context "C" do
    it "Bar" do
      baz = Baz.new('c')
      expect(baz.type).to eq('c')
    end
  end
end

You can instead use subject along with let to reduce the duplication:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
describe "Person" do
  subject { Person.new(name) } # Person has a get_name method
  context "Bob" do
    let(:name) { 'Bob' }
    its(:get_name) { should == 'Bob' }
  end
  context "Joe" do
    let(:name) { 'Joe' }
    its(:get_name) { should == 'Joe' }
  end
  context "Smith" do
    let(:name) { 'Smith' }
    its(:get_name) { should == 'Smith' }
  end
end

There are many more features available to RSpec but we’ve looked at the most important ones that you’ll find yourself using a lot when writing tests using RSpec.

Randomized Tests

You can configure RSpec to run your tests in a random order. This allows you to ensure that none of your tests have any reliance or dependency on the other tests around it.

1
2
3
RSpec.configure do |config|
  config.order = 'random'
end

You can set this via the command using the --order flag/option. For example: rspec --order random.

When you use the --order random option RSpec will display the random number it used to seed the algorithm. You can use this ‘seed’ value again when you think you’ve discovered a dependency issue within your tests. Once you’ve fixed what you think is the issue you can pass the seed value into RSpec (e.g. if the seed was 1234 then execute --order random:1234) and it will use that same randomized seed to see if it can replicate the original dependency bug.

Global Configuration

You’ve seen we’ve added a project specific set of configuration objects within ourRakefile. But you can set configuration options globally by added them to a .rspecfile within your home directory.

For example, inside .rspec:

1
--color --format nested

Debugging With Pry

Now we’re ready to start looking into how we can debug our application and our test code using the Pry gem.

It’s important to understand that although Pry is really good for debugging your code, it is actually meant as an improved Ruby REPL tool (to replace irb) and not strictly debugging purposes; so for example there are no built-in functions such as: step into, step over or step out etc that you would typically find in a tool designed for debugging.

But as a debugging tool, Pry is very focused and lean.

We’ll come back to debugging in a moment, but let’s first review how we’ll be using Pry initially.

Updated Code Example

For the purpose of demonstrating Pry I’m going to add more code to my example application (this extra code doesn’t effect our test in any way)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RSpecGreeter
  attr_accessor :test
  @@class_property = "I'm a class property"
  def greet
    binding.pry
    @instance_property = "I'm an instance property"
    pubs
    privs
    "Hello RSpec!"
  end
  def pubs
    test_var = "I'm a test variable"
    test_var
  end
  private
  def privs
    puts "I'm private"
  end
end

You’ll notice we’ve added some extra methods, instance and class properties. We also make calls to two of the new methods we’ve added from within our greet method.

Lastly, you’ll notice the use of binding.pry.

Setting Break-Points With binding.pry

A break-point is a place within your code where execution will stop.

You can have multiple break-points set within your code and you create them usingbinding.pry.

When you run your code you’ll notice that the terminal will stop and place you inside your application’s code at the exact spot your binding.pry was placed.

Below is an example of how it might look…

1
2
3
4
5
6
    8: def greet
=>  9:   binding.pry
   10:   pubs
   11:   privs
   12:   "Hello RSpec!"
   13: end

From this point Pry has access to the local scope so you can use Pry much like you would in irb and start typing in for example variables to see what values they hold.

You can run the exit command to exit Pry and for your code to continue executing.

Finding Where You Are: whereami

When using lots of binding.pry break points it can be difficult to understand where in the application you are.

To get a better context of where you are at any point, you can use the whereamicommand.

When run on its own you’ll see something similar to when you used binding.pry (you’ll see the line on which the break-point was set and a couple of lines above and below that). The difference being is if you pass an extra numerical argument like whereami 5you’ll see five additional lines above where the binding.pry was placed. You could request to see 100 lines around the current break-point for example.

This command can help orientate you within the current file.

Stack Trace: wtf

The wtf command stands for “what the f***” and it provides a full stack trace for the most recent exception that has been thrown. It can help you understand the steps leading up to the error that occurred.

Inspecting: ls

The ls command displays what methods and properties are available to Pry.

When run it will show you something like…

1
2
3
RSpecGreeter#methods: greet  pubs  test  test=
class variables: @@class_property
locals: _  __  _dir_  _ex_  _file_  _in_  _out_  _pry_

In the above example we can see that we have four public methods (remember we updated our code to include some additional methods and then test and test=were created when using Ruby’s attr_accessor short hand).

It also displays other class and local variables Pry can access.

Another useful thing you can do is to grep (search) the results for only what you’re interested in. You’ll need to have an understanding of Regular Expressions but it can be a handy technique. Here is an example…

1
2
ls -p -G ^p
=> RSpecGreeter#methods: privs

In the above example we’re using the -p and -G options/flags which tell Pry we only want to see public and private methods and we use the regex ^p (which means match anything starting with p) as our search pattern to filter the results.

Running ls --help will also show you all available options.

Changing Scope: cd

You can change the current scope by using the cd command.

In our example if we run cd ../pubs it’ll take us to the result of that method call.

If we now run whereami you’ll see it will display Inside "I'm a test variable".

If we run self then you’ll see we get "I'm a test variable" returned.

If we run self.class we’ll see String returned.

You can move up the scope chain using cd .. or you can go back to the top level of the scope using cd /.

Note: we could add another binding.pry inside the pubs method and then our scope would be inside that method rather than the result of the method.

Seeing How Deep You Are: nesting

Consider the previous example of running cd pubs. If we run the nesting command we’ll get a top level look on the number of contexts/levels Pry currently has:

1
2
3
4
Nesting status:
--
0. # (Pry top level)
1. "I'm a test variable"

From there we can run exit to move back to the earlier context (for example, inside the greet method)

Running exit again will mean we’re closing the last context Pry has and so Pry finishes and our code continues to run.

Locate Any Method: find-method

If you’re not sure where to find a particular method then you can use the find-methodcommand to show you all files within your code base that has a method that matches what you’re searching for:

01
02
03
04
05
06
07
08
09
10
11
find-method priv
=> Kernel
   Kernel#private_methods
   Module
   Module#private_instance_methods
   Module#private_constant
   Module#private_method_defined?
   Module#private_class_method
   Module#private
   RSpecGreeter
   RSpecGreeter#privs

You can also use the -c option/flag to search the content of files instead:

1
2
3
4
find-method -c greet
=> RSpecGreeter
   RSpecGreeter: def greet
   RSpecGreeter#privs:   greet

Classic Debugging: nextstepcontinue

Although the above techniques are useful, it’s not really ‘debugging’ in the same sense as what you’re probably used to.

For most developers their editor or browser will provide them with a built-in debugging tool that lets them actually step through their code line by line and follow the route the code takes until completion.

As Pry is developed to be used as a REPL that’s not to say it’s not useful for debugging.

A naive solution would be to set multiple binding.pry statements through out a method and use ctrl-d to move through each break-point set. But that’ still not quite good enough.

For step by step debugging you can load the gem pry-nav

01
02
03
04
05
06
07
08
09
10
11
12
13
14
gem 'rspec'
group :development do
  gem 'guard'
  gem 'guard-rspec'
  gem 'pry'
  # Adds debugging steps to Pry
  # continue, step, next
  gem 'pry-remote'
  gem 'pry-nav'
end

This gem extends Pry so it understands the following commands:

  • Next (move to the next line)
  • Step (move to the next line and if it’s a method, then move into that method)
  • Continue (Ignore any further break-points in this file)

Continuous Integration With Travis-CI

As an added bonus let’s integrate our tests with the online CI (continuous integration) service Travis-CI.

The principle of CI is to commit/push early and often to avoid conflicts between your code and the master branch. When you do (in this case we’re committing to GitHub) then that should kick off a ‘build’ on your CI server which runs the relevant tests to ensure all is working as it should be.

Now with TDD as your primary development methodology you’re less likely to incur bugs every time you push because your tests are an integral part of your development workflow and so before you push you’ll already be aware of bugs or regressions. But this doesn’t necessarily protect you from bugs that occur from integration tests (where all code across multiple systems are run together to ensure the system ‘as a whole’ is functioning correctly).

Regardless, code should never be pushed directly to your live production server any way; it should always be pushed first to a CI server to help catch any potential bugs that arise from differences between your development environment and the production environment.

A lot of companies have more environments still for their code to pass through before it reaches the live production server.

For example, at BBC News we have:

  • CI
  • Test
  • Stage
  • Live

Although each environment should be identical in set-up, the purpose is to implement different types of testing to ensure as many bugs are caught and resolved before the code reaches ‘live’.

Travis-CI

Travis CI is a hosted continuous integration service for the open source community. It is integrated with GitHub and offers first class support for multiple languages

What this means is that Travis-CI offers free CI services for open-source projects and also has a paid model for businesses and organizations who want to keep their CI integration private.

We’ll be using the free open-source model on our example GitHub repository.

The process is this:

  • Register an account with GitHub
  • Sign into Travis-CI using your GitHub account
  • Go to your “Accounts” page
  • Turn “on” any repositories you want to run CI on
  • Create a .travis.yml file within the root directory of your project and commit it to your GitHub repository

The final step is the most important (creating a .travis.yml file) as this determines the configuration settings for Travis-CI so it knows how to handle running the tests for your project.

Let’s take a look at the .travis.yml file we’re using for our example GitHub repository:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
language: ruby
cache: bundler
rvm:
  - 2.0.0
  - 1.9.3
script: 'bundle exec rake spec'
bundler_args: --without development
branches:
  only:
    - master
notifications:
  email:
    - you@example.com

Let’s break this down piece by piece…

First we specify what language we’re using in our project. In this case we’re using Ruby:language: ruby.

Because running Bundler can be a bit slow and we know that our dependencies aren’t going to change that often we can choose to cache the dependencies, so we setcache: bundler.

Travis-CI uses RVM (Ruby Version Manager) for installing Rubies on their servers. So we need to specify what Ruby versions we want to run our tests against. In this instance we’ve chosen 2.0 and 1.9.3 which are two popular Ruby versions (technically our application uses Ruby 2 but it’s good to know our code passes in other versions of Ruby as well):

1
2
3
rvm:
  - 2.0.0
  - 1.9.3

To run our tests we know that we can use the command rake or rake spec. Travis-CI by default runs the command rake but because of how Gems are installed on Travis-CI using Bundler we need to change the default command: script: 'bundle exec rake spec'. If we didn’t do this then Travis-CI would have an issue locating therspec/core/rake_task file which is specified within our Rakefile.

Note: if you have any issues regarding Travis-CI then you can join the #travis channel on IRC freenode to get help answering any questions you may have. That’s where I discovered the solution to my issue with Travis-CI not being able to run my tests using its default rake command and the suggestion of overwriting the default with bundle exec rake resolved that issue.

Next, because we’re only interested in running our tests we can pass additional arguments to Travis-CI to filter gems we don’t want to bother installing. So for us we want to exclude installing the gems grouped as development: bundler_args: --without development (this means we are excluding gems that are only really used for development and debugging such as Pry and Guard).

It’s important to note that originally I was loading Pry within our spec_helper.rb file. This caused a problem when running the code on Travis-CI, now that I was excluding ‘development’ gems. So I had to tweak the code like so:

1
require 'pry' if ENV['APP_ENV'] == 'debug'

You can see that now the Pry gem is only require‘ed if an environment variable ofAPP_ENV is set to debug. This way we can avoid Travis-CI from throwing any errors. This does mean that when running your code locally you’d need to set the environment variable if you wanted to debug your code using Pry. The following shows how this could be done in one line:

1
APP_ENV=debug && ruby lib/example.rb

There were two other changes I made and that was to our Gemfile. One was to make it clearer what gems were required for testing and which were required for development, and the other was explicitly required by Travis-CI:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
group :test do
  gem 'rake'
  gem 'rspec'
end
group :development do
  gem 'guard'
  gem 'guard-rspec'
  gem 'pry'
  # Adds debugging steps to Pry
  # continue, step, next
  gem 'pry-remote'
  gem 'pry-nav'
end

Looking at the above updated Gemfile we can see we’ve moved the RSpec gem into a new test group, so now it should be clearer what purpose each gem has. We’ve also added a new gem 'rake'. The Travis-CI documentation states this needed to be specified explicitly.

The next section is optional and it allows you to white list (or black list) certain branches on your repository. So by default Travis-CI will run tests against all your branches unless you tell it otherwise. In this example we’re telling it we only want it to run against ourmaster branch:

1
2
3
branches:
  only:
    - master

We could tell it to run every branch ‘except’ a particular branch, like so:

1
2
3
branches:
  except:
    - some_branch_I_dont_want_run

The final section tells Travis-CI where to send notifications to when a build fails or succeeds:

1
2
3
notifications:
  email:
    - you@example.com

You can specify multiple email addresses if you wish:

1
2
3
4
5
notifications:
  email:
    - you@example.com
    - them@example.com
    - others@example.com

You can be more specific and specify what you want to happen on either a failure or a success (for example, more people will only be interested if the tests fail rather than getting an email every time they pass):

1
2
3
4
5
6
notifications:
  email:
    recipients:
      - you@example.com
    on_failure: change
    on_success: never

The above example shows that the recipient will never receive an email if the tests pass but will get notified if the failure status changes (the default value for both is alwayswhich means you’ll always be notified regardless of what the status result).

Note: when you explicitly specify a on_failure or on_success you need to move the email address(es) inside of recipients key.

Conclusion

This is the end of our two part look into RSpec, TDD and Pry.

In part one we managed to write our application using the TDD process and the RSpec testing framework. In this second half we’ve also utilized Pry to show how we can more easily debug a running Ruby application. Finally we were able to get our tests set-up to run as part of a continuous integration server using the popular Travis-CI service.

Hopefully this has given you enough of a taste so you are keen to investigate each of these techniques further.