Getting started with RSpec — Part 2

Andy
4 min readSep 1, 2017

Note: This is the second and final installment of the RSpec — Getting Started series. If you haven’t read the first part, please go back (link) and read it before continuing.

In the previous edition of “Getting Started with RSpec” we made a simple calculator with unit tests. We saw how tests can help us maintain code even when working with pretty dumb programmers.

In this installment we are going to be focusing on Test Driven Development (TDD). What is TTD?

Test Driven Development is the workflow of writing a test, letting it fail, and then changing your code to make it (and all the previous tests) pass.

Enough talk, let’s get coding

To demonstrate let’s add a subtract method to our calculator. To do this following a TDD workflow, we first add a test with our expectations:

# File: spec/calculator_spec.rbrequire './calculator.rb'describe "calculator" doit 'adds numbers' do
...
end
it 'subtracts numbers' do
calc = Calculator.new
expect(calc.subract(5,1)).to eql(4)
end
end

This new subtracts numbers test is expecting that when we call the subtract function and pass it two parameters, 5 and 1 we will be returned a value of 4

Let’s run the tests to make sure we don’t have any syntax errors:

calculator $ bundle exec rspec
.F
Failures:1) calculator subtracts numbers
Failure/Error: expect(calc.subtract(5,1)).to eql(4)

NoMethodError:
undefined method `subtract' for #<Calculator:0x007f9dc4a65f40>
# ./spec/calculator_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 0.00236 seconds (files took 0.08835 seconds to load)
2 examples, 1 failure
Failed examples:rspec ./spec/calculator_spec.rb:9 # calculator subtracts numbers

Just what we expected. One of our tests has failed. We can see which test has failed in the last line of the output.

Now that we have written our test and have seen it fail, it’s time to make it pass.

Let’s create a subtract method in our Calculator:

# File: calculator.rbclass Calculator
def add(x, y)
x + y
end

def subtract(x, y)
x - y
end
end

The last step in TDD is to make all the tests pass. This is sometimes called going “Green,” since the output of passing tests is green. To see if our Calculator behaves as we want it to, run the tests:

calculator $ rspec
..
Finished in 0.00224 seconds (files took 0.08993 seconds to load)
2 examples, 0 failures

Perfect! You’ve just completed your first TDD cycle! That wasn’t too difficult, was it?

Now that you’re feeling confidant in your abilities, let’s repeat this process for themultiply and divide functions. Let’s start with multiply :

# File: spec/calculator_spec.rbrequire './calculator.rb'describe 'calculator' do
it 'adds numbers' do
...
end
it 'subtracts numbers' do
...
end
it 'multiplies numbers' do
calc = Calculator.new
expect(calc.multiply(3,4)).to eql(12)
end
end

Make sure your tests fail by running rspec

Then change your code so your tests pass:

# File: calculator.rbclass Calculator
def add(x, y)
x + y
end
def subtract(x, y)
x - y
end
def multiply(x, y)
x*y
end
end

Do your tests now pass?

calculator $ rspec
...
Finished in 0.00262 seconds (files took 0.08638 seconds to load)
3 examples, 0 failures

Lookin’ good! Lastly let’s finish with our divide function:

# File: spec/calculator_spec.rbrequire './calculator.rb'describe 'calculator' do
it 'adds numbers' do
...
end
it 'subtracts numbers' do
...
end
it 'multiplies numbers' do
...
end
it 'divides numbers' do
calc = Calculator.new
expect(calc.divide(5,2)).to eql(2.5)
end
end

Watch your test fail and then add the divide method:

# File: calculator.rbclass Calculator
def add(x, y)
x + y
end
def subtract(x, y)
x - y
end
def multiply(x, y)
x*y
end
def divide(x, y)
x / y
end
end

One final check gives us:

calculator $ rspec
...F
Failures:1) calculator divides numbers
Failure/Error: expect(calc.divide(5,2)).to eql(2.5)

NoMethodError:
undefined method `divide' for #<Calculator:0x007fc9c0345440>
# ./spec/calculator_spec.rb:23:in `block (2 levels) in <top (required)>'
Finished in 0.00278 seconds (files took 0.09446 seconds to load)
4 examples, 1 failure
Failed examples:rspec ./spec/calculator_spec.rb:21 # calculator divides numbers

Whoops! That’s clearly not right. We were expecting all our tests to pass. What’s going on here?

To get more information about the error, we can add a handy feature of RSpec, output formatting. In the console run the command rspec --format documentation and you should get this:

calculator $ rspec
...F
Failures:1) calculator divides numbers
Failure/Error: expect(calc.divide(5,2)).to eql(2.5)

expected: 2.5
got: 2

(compared using eql?)
# ./spec/calculator_spec.rb:23:in `block (2 levels) in <top (required)>'
Finished in 0.01352 seconds (files took 0.09081 seconds to load)
4 examples, 1 failure
Failed examples:rspec ./spec/calculator_spec.rb:21 # calculator divides numbers

We were expecting the value 2.5 to be returned, but instead we received a value of 2

This is where TDD really shines bright. If we hadn’t had these tests in place, we would have been in trouble when we needed to use the divide function in production.

To fix this problem, we need to change one the divde inputs to Float with the .to_f function:

def divide(x, y)
x / y.to_f
end

Now that we’ve made a change, we rerun the tests:

calculator $ rspec
....
Finished in 0.00271 seconds (files took 0.09715 seconds to load)
4 examples, 0 failures

That’s better. Now that all of our tests are passing, we can call it a day. Great work!

Test Driven Development is an important tool in any developer’s toolbox. It allows us to develop with the confidence that our code is not going to break if we change something. It’s especially helpful when refactoring our code to ensure that all the functions work properly. Refactoring is almost impossible without test to back it up. If you have any questions about TDD please feel free to leave a comment below

--

--

Andy

Web Developer, Marathon Runner, Coffee Drinker.