How to build a simple Ruby Calculator app with RSpec tests using TDD

How to build a simple Ruby Calculator app with RSpec tests using TDD

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:

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 install bundler 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 called calculator_spec.rb. This is where you'll test your Calculator class.

The First RSpec Test

  • In the calculator_spec.rb file, write a describe 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 command rspec

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:

Screen Shot 2021-03-21 at 2.05.14 PM.png

  • The last thing you'll need to do is import the class file into your spec_helper file so that all of your spec files can access it for testing. Paste this into the very top of your spec_helper file:
#frozen_string_literal: true
require './calculator'
  • Now, when you run your test using rspec or rspec 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 as calculator.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

Screen Shot 2021-03-21 at 2.28.47 PM.png

Screen Shot 2021-03-21 at 2.29.10 PM.png

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!