Blog

setting up TDD and continuous integration

Written by Thomas Henderson
Published on 07 August 2017

Today we’re going to set up a new project in Ruby. Me, I’m programming John Horton Conway’s Game of Life.

In this post, we’re going to go through the process of: starting a Ruby project from nothing; adding RSpec; adding just enough tests and code to see if we’re testing anything; synchronizing our project to Github; and, finally, authorizing Travis-CI to read our repository and run tests whenever we push our changes.

Creating the project

Create a directory where the project should live.

mkdir game-of-life
cd game-of-life

Open a file called Gemfile, to tell Ruby where to find gems and what gems we need.

source "https://rubygems.org"
gem "rspec"

Make sure we have the Ruby program for fetching and installing gems…

gem install bundler

…and then do so.

bundle install --path .bundle

The --path flag installs it to a specific directory; that way, we don’t have to use sudo to install the bundle with root privileges, which could get us into some difficult-to-diagnose trouble.

Testing whether we’re testing

Now let’s write our first test. Make a specification directory:

mkdir spec

and in that directory, write your first test. Right now we want to test whether we’re testing, so a trivial test that exists but has no assertions will do.

describe TrivialTest do
end

We can run the test with bundle exec rspec. The test will fail, because there is no TrivialTest class. To pass the test, create a directory for our source code in the project root directory:

mkdir lib

And inside that directory, create an rspec_works.rb file:

class TrivialTest
end

If you like you can run bundle exec rspec again. The test will fail again, for a different reason: the class has been defined, but the spec file doesn’t know about it. We need a require statement. Modify rspec_works_spec.rb like so:

require "rspec_works"
describe TrivialTest do
end

Run bundle exec rspec again, and the test will pass.

Synchronizing our project with Github.com

Next we commit our work. If you haven’t already, initialize the directory as a git repository with git init. Now we can start tracking our working files with git add Gemfile lib spec, followed by git commit. Or if you want to write a one-line commit message without git sending you to your editor, write something like git commit -m "Pass trivial test" after adding the files.

Now we can begin integrating with Github and Travis.

Start by opening your Github profile, signing in if you haven’t already, and clicking the ‘plus’ icon. Give your repository a name – you’ll probably want to match the name you gave to the project directory – and a description, then click “Create Repository.” Do NOT check the box to “Initialize this repository with a README.”

Once you’ve done this, a tutorial page will appear; follow the directions marked “…or push an existing repository from the command line.” If you don’t have an ssh key set up, you’ll have to enter your login credentials for Github. (If you don’t set up an ssh key, you will quickly get over having to enter this every time you want to push work, but that’s a tutorial for another time.)

Now, we’re ready to set up continuous integration with Travis.

Setting up continuous integration with Travis-CI

There is a Getting Started with Travis CI tutorial, but I found some discrepancies between it and reality, possibly just from my own misunderstanding. The following is what worked for me.

Go to https://travis-ci.org and sign in with your Github account. Then go to the repository that you created, follow the Settings link, then the Integrations & services link. Or, just enter https://github.com/Your_Github_Name/Your_Repo_Name/settings/installations into your browser. Slick the Add Service dropdown menu, and select Travis. (There’s a lot of options but you can filter the options.) You will be prompted to confirm your password.

You’ll be presented with a page that has a few text fields which I ignored. Follow the link to https://travis-ci.com/profile. If you’ve never done this before, I believe that this is when Travis-CI will attempt to load your Github repositories. (Note: As of this writing, having an unpaid balance with Github will keep Travis-CI from completing this action, and it will just keep trying to import with no success forever. If you have a lapsed paid account with Github, you’ll have to cough up some money before this step will work).

Assuming your repos have been successfully imported, Travis-CI will present you with a list of your repos, with a checkmark slider for each. Check the repo(s) you’d like Travis to watch.

Instructing Travis-CI what to do with your project

We’re almost there. Your project is on the remote server owned by Github, and Travis-CI has been authorized to read it from there. But Travis doesn’t have enough information to do anything with your code. It needs some directives to know how to behave when you push code to the repo.

In the root of your project directory, open .travis.yml. Here’s the simple version I used:

language: ruby
rvm:
- 2.4.0
install: gem install rspec
script: bundle exec rspec

This let’s Travis know that we’re using Ruby, that we’re using a particular version, that we’ll need to have rspec installed before we take action, and that the action to take is to run our test suite. (~Note~: As of this writing, there is something with Ruby 2.4.1 that interferes with Travis-CI’s ability to write gems, so I had to walk the version back to 2.4.0. I did not bother changing the version of Ruby I was using to write my project though. So far, so good, but your mileage may vary.)

Let’s find out if it works. Add .travis.yml to your repository, commit, and push. Now if you click the link to your repo, or go directly to https://travis-ci.org/YourName/YourProject/builds

…well, probably nothing will appear to be happening. Be patient. Soon enough, there should be an entry with your most recent commit message, marked in yellow to signify that the build is still in progress. In about a minute, assuming that you only have that one passing test in your code, the build will be marked with green, and the message, “#1 passed.”

Hooray! Your tests all passed, and your integration is complete.

But… why?

This is probably 100 times slower than it was to run the tests locally. So why bother? There’s several reasons. First, when your tests are numerous enough that they may take several minutes to run, this way you can keep developing code while your tests run off of your machine. Second, it’s easy enough to run unit tests on your own machine, but Travis could, for example, run tests that involve resources you don’t control. Maybe a change in a dependency breaks your project when it’s deployed even though it works fine locally, or there’s a difference in behaviors when you switch from your test database to your production database.

But perhaps most intriguing is, you can set up triggers for different behaviors for Travis to initiate based on the passing or failing of tests. For example, you might edit your blog, and when you push changes, set up Travis to notify you if there are broken links in the blog, and deploy the blog if and only if the tests pass. Me, I went to my company’s Slack channel, typed /apps travis, and got a link to instructions on how to create a bot that will post to the private apprenticeship channel I’m in with my mentors, so that they are notified when I’ve pushed changes for one of my assigned projects. It’s simple, but useful. (If you do this, make sure to look into using the Travis-CI command line tool to encrypt the necessary tokens – after all, they will be getting pushed to your repo, and you don’t want to put secrets up there.)