Skip to content

Instantly share code, notes, and snippets.

@grahamlyons
Last active August 7, 2017 09:47
Show Gist options
  • Save grahamlyons/e3ccedb80d8975c6cb1e2de07afc2e92 to your computer and use it in GitHub Desktop.
Save grahamlyons/e3ccedb80d8975c6cb1e2de07afc2e92 to your computer and use it in GitHub Desktop.
Getting started with RSpec - an example using FizzBuzz

Getting started with RSpec

Assumptions

  • a *nix system
  • Ruby installed - rvm (curl -sSL https://get.rvm.io | bash) is useful for installing the latest version and keeping gems local to your user
  • a directory called workspace in your home directory
  • Atom editor installed - https://atom.io/

First steps

  • cd ~/workspace/ (go to your workspace)
  • mkdir fizzbuzz (make a new directory)
  • cd fizzbuzz (change into it)
  • gem install bundler (install a gem (1) called 'bundler')
  • bundle init (run Bundler (2) program, passing the init argument - this creates a file called Gemfile)
  • atom Gemfile (open the file in Atom)
  • Change gem "rails" to gem "rspec" and go back to the command line (this tells Bundler that we want to install a gem called RSpec)
  • bundle install (run Bundler again, this time passing it the install argument)
  • bundle exec rspec --init (tell the bundler program to run the rspec program, passing the --init argument to it)
  • mkdir lib (make another directory to hold the actual code)
  • touch lib/fizzbuzz.rb (create a file that we'll use later)
  • atom spec/fizzbuzz_spec.rb (open a file to hold our tests - the spec directory was created by rspec --init)

In the file you've just opened, require the fizzbuzz file and start a description for it:

require 'fizzbuzz'

describe FizzBuzz do
end

We are describing something with the name FizzBuzz so that clearly needs to exist.

Open lib/fizzbuzz.rb in Atom e.g. run atom lib/fizzbuzz.rb on the command line and then create the FizzBuzz module:

module FizzBuzz
end

Aside: by putting the fizzbuzz.rb file into the lib directory, RSpec can find it so we can include it simply with require 'fizzbuzz', rather than having to tell the test file where it is on the file-system with a relative or absolute path e.g. require '../fizzbuzz.rb' or require '/Users/billbums/workspace/fizzbuzz/fizzbuzz.rb'.

At this point we can run RSpec and see the output: bundle exec rspec

You should see something which looks like this:

No examples found.


Finished in 0.00029 seconds (files took 0.07641 seconds to load)
0 examples, 0 failures

So everything runs, we just haven't written any tests yet.

Writing some actual tests

Next, let's write a test. If it's not already, open up the fizzbuzz_spec.rb file in the spec directory: atom spec/fizzbuzz_spec.rb and add a test to it. We'll test the basic case when we pass 1 to a function called fizzbuzz and the test will look like this:

it "returns 1 for 1" do
  expect(FizzBuzz.fizzbuzz(1)).to eq(1)
end

It needs to go inside the describe block, so the whole thing will look like this:

 describe FizzBuzz do
   it "returns 1 for 1" do
     expect(FizzBuzz.fizzbuzz(1)).to eq(1)
   end
 end

OK, so what have we written? Well, we've called the fizzbuzz method on the FizzBuzz module and passed 1 into it: FizzBuzz.fizzbuzz(1).

We've passed that whole thing into a method called expect: expect(FizzBuzz.fizzbuzz(1))

Then we've called to on whatever that returns: expect(FizzBuzz.fizzbuzz(1)).to

And then, finally, we've said that we want that to be equal to 1 by using the eq method: eq(1)

(Notice that we have left the brackets off the to method to make this read more like a sentence, which is a convention that's often used).

Seeing a failing test

We can run the tests again, now that one exists.

Run with: bundle exec rspec

The output should look something like this:

F

Failures:

  1) FizzBuzz returns 1 when passed 1
     Failure/Error: expect(FizzBuzz.fizzbuzz(1)).to eq(1)

     NoMethodError:
       undefined method `fizzbuzz' for FizzBuzz:Module
     # ./spec/fizzbuzz_spec.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.00179 seconds (files took 0.09138 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/fizzbuzz_spec.rb:4 # FizzBuzz returns 1 when passed 1

The test has failed because the method we're calling doesn't exist. That's fine - we'd like to see the test fail first before we make it pass.

TDD? WTF?

Test Driven Development (TDD) is a software development practice which involves writing a test first, before the code to make it pass actually exists.

You write the test first, run it to see it fail, write the code to make it pass then run the test again and see that all is well (ideal world).

This has several benefits:

  • you know that what you've written definitely satisfies your test
  • you can write just enough code to make the tests pass leading to less complexity and full test coverage
  • your code has tests (it's much harder to go back and write them retrospectively)

Making the test pass

Finally (for this introduction) we can make this test pass.

Adding this simple method inside the FizzBuzz module will make our first test pass (we have to add more tests for the rest of FizzBuzz):

def self.fizzbuzz(n)
  n
end

Run the tests again, as before, and see your triumph.

Footnotes

Gems?

Ruby code can be wrapped up into a package so that it can be installed and used in other programs, projects and by other people. These packages of code are called 'Gems'. Reusing code that other people have written is essential if you want to get anything done... See https://www.ruby-lang.org/en/libraries/

Bundler?

We've installed, via a Gem, a program called Bundler. We can give Bundler a list of Gems (in the `Gemfile`) that we want to install and it will manage the installation of all of them. (Imagine you had to install 10 different Gems - running 'gem install ...' 10 times would be tedious and error-prone).

Bundler will also make sure that your program can find the Gems to be able to use, plus allow you to run programs installed via Gems e.g. the rspec program.

Bundler also generates a file called Gemfile.lock, which you should checkin to source control (Git). It contains the specific versions of the dependencies which were installed, so if you install RSpec version 1.2.3 and everything works then the next person to follow your instructions should get the same version to avoid any unforseen problems.

The Bundler site is here: http://bundler.io/

module FizzBuzz
def self.fizzbuzz(n)
n
end
end
require 'fizzbuzz'
describe FizzBuzz do
it 'returns 1 when passed 1' do
expect(FizzBuzz.fizzbuzz(1)).to eq(1)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment