Today we will learn how to test our Ruby code using a tool called RSpec.
RSpec is a test framework for Ruby. RSpec is like a helpful robot assistant. You tell it what your code should do, and it checks if your code actually does that. It's a way to make sure your program behaves the way you expect it to.
Install: $ gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
A simple example
Let’s say we have a simple calculator program.
# calculator.rb
class Calculator
def add(a, b)
a + b
end
end
How do you know it's working correctly? You'd probably try adding some numbers and see if you get the right answer. That's exactly what testing is all about - but we're letting the computer do the checking for us!
RSpec uses the words "describe" and "it" so we can express concepts like a conversation:
"Describe a Calculator."
"It correctly adds two numbers."
Don't worry about remembering exact commands or syntax - let's focus on the big picture!
Here is how we can use RSpec for testing. We call the add method with 2 and 3. We expect the result to equal 5.
# calculator_spec.rb
require_relative 'calculator'
RSpec.describe Calculator do
describe '#add' do
it 'correctly adds two numbers' do
calc = Calculator.new
result = calc.add(2, 3)
expect(result).to eq(5)
end
end
end
To run this test, you would type $ rspec calculator_spec.rb
in your terminal. If everything is working correctly, you'll see a green dot, indicating that the test passed!
RSpec::Expectations provides a simple, readable API to express the expected outcomes in a code example. To express an expected outcome, wrap an object or block in expect, call to or to_not (aliased as not_to) and pass it a matcher object. Read more https://rspec.info/documentation/3.13/rspec-expectations/RSpec/Expectations.html
RSpec::Matchers provides a number of useful matchers we use to define expectations. Any object that implements the matcher protocol can be used as a matcher. Read more https://rspec.info/documentation/3.13/rspec-expectations/RSpec/Matchers.html
When we test our calculator's addition function, we want to make sure it works in all sorts of situations. Here are some ideas of what we might want to check:
Here's an enhanced version of the RSpec code:
# calculator_spec.rb
require_relative 'calculator'
RSpec.describe Calculator do
let(:calculator) { Calculator.new }
describe '#add' do
it 'correctly adds two positive numbers' do
expect(calculator.add(2, 3)).to eq(5)
end
it 'correctly adds a positive and a negative number' do
expect(calculator.add(5, -3)).to eq(2)
end
it 'correctly adds two negative numbers' do
expect(calculator.add(-2, -3)).to eq(-5)
end
it 'returns zero when adding zero to zero' do
expect(calculator.add(0, 0)).to eq(0)
end
it 'returns the same number when adding zero' do
expect(calculator.add(7, 0)).to eq(7)
expect(calculator.add(0, 7)).to eq(7)
end
it 'correctly adds decimal numbers' do
expect(calculator.add(1.5, 2.7)).to be_within(0.01).of(4.2)
end
it 'correctly adds large numbers' do
expect(calculator.add(1000000, 2000000)).to eq(3000000)
end
end
end
By including these additional tests, we're ensuring that our add method works correctly for a wide range of inputs. This is called "edge case testing" - we're not just testing the obvious cases, but also the less common or potentially problematic ones.
To run these tests, you would still use the command rspec calculator_spec.rb in your terminal. If all tests pass, you'll see a series of green dots, one for each test. If any test fails, you'll see a red F instead of a green dot, along with information about what went wrong.
When you're writing tests, try to think like a detective. What are all the ways your code might be used? What could go wrong? By anticipating these scenarios, you can write better tests and, as a result, better code.
As your programs get bigger and more complex, it's easy for new changes to accidentally break something else. Tests help catch these problems early.