Why I Built This Project
Recently at work, I was promoted to a hybrid SDET/functional QA role. Hooray!! Leading up to this promotion, I did a lot of preparation work. I wanted to do automation QA stuff, but I didn't like the idea of having to learn every single thing on the job from scratch. Where I work, knowing the technologies for a role isn't always a prerequisite for internal promotions, but doing some self-directed learning usually helps to reduce the amount of stress you experience once you're actually in the role doing the things. I thought it would make sense to know some Ruby and RSpec prior to seeking to move into an automation role that used those technologies.
As part of this preparation, I took a couple of courses:
- Learn Ruby - CodeCademy (Pro version free trial to get access to the lesson projects)
- Testing Ruby with RSpec: The Complete Guide
These courses were amazing and I highly recommend them. That said, I wanted to build my own Ruby project from scratch and add RSpec tests to it. To me, that would be a "proof of concept" in terms of my ability to apply unit testing to my own code. I figured, if I could unit-test my own code, I could unit-test someone else's code, too.
The project I chose to tackle was a Ruby Calculator, by the suggestion of the QA Automation Lead at my work. I had already built a React calculator with a conditionally-rendered UI prior to this, so building a calculator that worked off user input from a CLI didn't seem too crazy. It wasn't a random quote generator, but it was challenging enough to be worth the effort.
Setting up the Project
Make sure you have Ruby installed on your computer. If you don't, here's a link to where you can download it -> ruby-lang.org/en/downloads
Install RSpec into your project using their video tutorial -> rspec.info
Create a file called Gemfile and copy this into it:
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'rspec'
gem 'rubocop'
gem 'rubocop-rspec'
From the project directory, run
gem install bundler && bundle install
to installbundler
and then install all of these gems. Rubocop is a Ruby style enforcer that you can use to check your project's syntax. It's helpful if you aren't familiar with the style guide/are new to Ruby.In your project directory, create a file called
calculator.rb
. This is where you'll build your Calculator class.In the
spec
directory that your RSpec installation should have created, create a new spec file calledcalculator_spec.rb
. This is where you'll test your Calculator class.
The First RSpec Test
- In the
calculator_spec.rb
file, write adescribe
block for testing the Calculator class, like this:
# frozen_string_literal: true
RSpec.describe Calculator do
let(:calc) { described_class.new }
end
- As per TDD, you'll want to write the test before you write the feature. So, we'll use the addition operation as the first test to show how this works:
it 'does addition' do
expect(calc.add(5, 9)).to be(14)
end
Put this code into your describe
block, and your file should now look like this:
# frozen_string_literal: true
RSpec.describe Calculator do
let(:calc) { described_class.new }
it 'does addition' do
expect(calc.add(5, 9)).to be(14)
end
end
- Run this test, and it should fail. To run this test, run this command in the project directory:
rspec spec/calculator_spec.rb
or, since it's the only test you have, you can just run all the tests using the commandrspec
Making the Calculator
- To make this test pass, obviously, we need to build the feature it's testing and make sure that feature does what we want it to do. Over in the
calculator.rb
file, paste in this code:
class Calculator
def add(num1, num2)
num1 + num2
end
end
This is the Calculator class with a method that takes 2 numbers from the user and returns the sum of the 2 numbers.
If you run the test again, it will still fail, because we haven't made an instance of the Calculator class yet. Right now, we just have the definition. We also don't have any user input yet, so the Calculator instance wouldn't have any numbers to work with.
Let's add user input now. Paste this code below the Calculator class definition:
puts 'Enter the first number'
first_num = gets.chomp.to_i
puts "From this list, please enter the operation you'd like to perform:"
puts '+ - / * ^ %'
operation = gets.chomp
puts 'Enter the second number'
second_num = gets.chomp.to_i
Notice how we're building this with other math operations in mind, such as subtraction and modulo. We are trying to make this Calculator class scalable so we can include other features. That's also why we made the addition feature a method inside the class, so we can just add more methods if we want other operations later on.
- We now have to create an instance of the Calculator class so we can use the methods to give the user some output:
calculator = Calculator.new
- Next, we'll set up a
case-when
statement that will let the Calculator make different decisions depending on which operation the user chose to perform:
case operation
when '+'
puts "Result is #{calculator.add(first_num, second_num)}"
end
Running the Program
- Now, let's try this feature out by running
ruby calculator.rb
from the project directory:
The program asks you for a number, an operation, and another number. Then it spits out your result. It should look like this:
- The last thing you'll need to do is import the class file into your
spec_helper
file so that all of yourspec
files can access it for testing. Paste this into the very top of yourspec_helper
file:
#frozen_string_literal: true
require './calculator'
- Now, when you run your test using
rspec
orrspec spec/calculator_spec.rb
, you should be prompted to enter the numbers and operation, and your test should pass.
Refactoring the Project
When writing this tutorial, I realized I was placing the execution logic inside of the class file, which isn't necessary and probably not good practice. You can do the same thing I did to improve the project for yourself.
- Create a new file called
app.rb
at the same level ascalculator.rb
- In that file, cut & paste the logic from
calculator.rb
that runs the app, making sure to import (require
) the Calculator class file:
require './calculator'
puts 'Enter the first number'
first_num = gets.chomp.to_i
puts "From this list, please enter the operation you'd like to perform:"
puts '+ - / * ^ %'
operation = gets.chomp
puts 'Enter the second number'
second_num = gets.chomp.to_i
calculator = Calculator.new
case operation
when '+'
puts "Result is #{calculator.add(first_num, second_num)}"
end
- Whenever you want to run tests, run
rspec
and you won't have to enter the numbers anymore because the only execution logic lives inside your tests - Whenever you want to run the app with your own numbers and operation selections, run
ruby app.rb
and input your numbers and operation
At this point, the last step is to add tests for new operations, and then build those new operations into the calculator class. The project in this tutorial is set up to handle addition, subtraction, division, multiplication, modulo, and exponentiation, but you can feel free to add whatever you want to expand on it. I even encourage you to refactor it if you see a better way to implement the calculator.
Every method you add to the calculator class can be run with your own input and tested in the calculator_spec.rb
file without any further configuration changes. Have fun, and happy testing!