Basic Ruby Calculator with RSpec
Tags: ruby, rspec
Published: 2021-03-05 02:28:21

Testing is an important part of Software Development, it can be slow to start especially as you're learning it, but it is such an important savior in the long run. As projects get bigger you can run tests to make sure new features don't break old ones and changes don't have unexpected consequences. 

To get started I decided to learn and get some practice using RSpec, which is a unit test framework for the Ruby programming language. RSpec is a Behavior-driven development tool, meaning that tests written in RSpec focus on the "behavior" of an application being tested. RSpec doesn't put emphasis on, how the application works but instead on how it behaves, in other words, what the application actually does.

Here is my first try at building a simple ruby calculator using test-driven development(TDD)! TDD is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases. It is sometimes referred to as red-green testing because you write tests for what you want the code to do, then you run the tests to see them fail (red). Then you write the code to make the tests pass(green). 

To start, make sure you have ruby and RSpec installed. You can check that is the case by running 

```
$ ruby -v
ruby 2.6.1p33 (2019-01-30 revision 66950)
```
and 

```
$ rspec -v
RSpec 3.10
  - rspec-core 3.10.1
  - rspec-expectations 3.10.1
  - rspec-mocks 3.10.2
  - rspec-support 3.10.2
```
If you are missing either of these you can check out the documentation for [Ruby](https://www.ruby-lang.org/en/documentation/installation/) and to add RSpec you can run 

```
$ gem install rspec
```
A gem is a Ruby library that you can use in your own code and you can install these using the gem command. 

Next, let's make a folder in a location of your choice called calculator.

 ```
$ mkdir calculator
```

Let's cd into the folder and open it up with your code editor.
```
$ cd calculator
$ code .
```

Next we are going to need a Gemfile that knows which version of RSpec to run. 

```
$ touch Gemfile
```
In that gem file add ```source "https://rubygems.org"``` so it knows where to get its gems from. Then in your terminal 
```
$ bundle add rspec
```
and your gemfile will be updated to have the latest version of rspec available to your project.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irw2ajg8xux7j8q476f3.png)

Now we can make folders called lib to hold the Calculator class and spec to hold the tests which is a common practice.

```
$ mkdir lib
$ mkdir spec
```
Now for the fun part! Let's make a file called calculator_spec.rb, it's common practice to have your test file have the name of the file it's testing plus _spec. And another file called calculator.rb in your lib folder.

First we are going to require the file that we are testing at the top of the spec file:

```ruby
require './lib/calculator.rb'
```
Now let's write our first test! We want our Calculator to be able to add two numbers and return the result. 

```ruby
describe Calculator do 
  context "Given two numbers" do 
    it "adds the numbers using the add method" do
      calc = Calculator.new
      sum = calc.add(2,3)
      expect(sum).to eql(5)
    end
  end
end
```
Woah, Nelly! What's all that! Ok. So. The word **describe** is an RSpec keyword. It is used to define an “Example Group”, which is a collection of tests. The **describe** keyword can take a class name and/or string argument, in our case we used the Calculator class name (which doesn't exist yet). You also need to pass a block argument to describe, this will contain the individual tests, or as they are known in RSpec, the “Examples”. The block is just a Ruby block designated by the Ruby do/end keywords.

So our describe Calculator block will wrap the individual tests. Inside the describe block is **context**, another RSpec keyword. It is very similar to the describe block, and is not mandatory, but it can help add more details to what the tests are doing. In this case, we are just testing for handling two numbers entered into the function.

The word **it** is another RSpec keyword used to define an “Example”, ie a test case. **it** is passed a string and a block argument designated with do/end (like describe and context, it can accept a class, but it would be unusual to do so). The string of **it** describes the expected outcome for the test. The string makes it clear what should happen when we call add on an instance of the Calculator class. As part of the RSpec philosophy, an Example is not just a test, it’s also a specification (a spec). So the Example both documents and tests the expected behavior of your Ruby code.

We then make an instance of the Calculator class so that we can call the add method on it to test if it does actually add together properly. Let's set a variable of sum equal to the result of passing in the numbers 2 and 3 into the add method.

And now, the actual test! The expect keyword is used to define an “Expectation” in RSpec. This is where we verify, that a specific expected condition has been met. The idea is that expect statements should read like normal English. You can say this aloud as “Expect the sum to equal 5”. The idea is that it's descriptive and easy to read.

The **to** keyword is used as part of expect statements. **to** is used with a dot, expect(sum).to, because it actually just a regular Ruby method. In fact, all of the RSpec keywords are really just Ruby methods. 

And finally, we have the **eql** keyword which is a particular kind of RSpec keyword called a Matcher. Matchers are used for specifying what type of condition you are testing to be true (or false). Here we are expecting the add method to return an integer that matches 5.

All right! Let's run our test with the command rspec and the file designated in the spec folder! 

```
$ rspec spec/calculator_spec.rb   
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1mq7ba64h48tsm1ym1t.png)

Our error tells us that the constant Calculator is uninitalised, which makes sense because we haven't built our Calculator class yet! So in the calculator.rb file, let's make our class: 

```ruby
class Calculator 
end
```
 and run the tests again.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8skdxcztr17ww1s7uf9.png)

Woohoo! 1 failure! This is good. We are in the red part of red/green testing. There's no method add. So let's fix that.

```ruby
def add
end
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbig1o9oao2h2ncchmhp.png)

Now our test is telling us that the add method received 2 arguments, but was expecting 0. This is because in the test we gave it two, but in the actual code it doesn't have any arguments passed into the method So let's pass it two arguments.

```ruby
 def add(x,y)
 end
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mq1dij4cvo5qpnjwg6tl.png)

Now the test was expecting 5 to be returned, but it failed because we didn't return anything. Let's fix that.  

```ruby
  def add(x,y)
    return 5
  end
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gldpzmvu30thmx3bxlur.png)

Woo! It passed! But...wait...that's silly, isn't it? Well, yes, you're right. If you write your tests and make them pass with the minimum amount of work, and it doesn't actually behave how you want, then you need to upgrade your tests to be a bit more robust. Let's add another test!

```ruby
    it "can add two different numbers" do 
      calc = Calculator.new
      sum = calc.add(8,23) 
      expect(sum).to eql(31)
    end
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z517g4b2ce8v32hyfgx7.png)
Ok, so we're passing the first test, but not the second. Let's fix is so we pass both!

```ruby
  def add(x,y)
    return x + y
  end
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0p3iwk33x2q7vu5cngb.png)
Yes! Two passing tests! 

Did anyone else notice that we initialized a new instance of the calculator class inside both **it** blocks? We can refactor this to using the RSpec before hook which takes in a symbol indicating the scope and a block of code to execute. The before(:each) block will run before each example.

```ruby 
describe Calculator do 
  
  before(:each) do 
    @calc = Calculator.new 
  end

  context "Given two numbers" do 
    it "can add the numbers using the add method" do
      sum = @calc.add(2,3)
      expect(sum).to eql(5)
    end

    it "can add two different numbers" do 
      sum = @calc.add(8,23) 
      expect(sum).to eql(31)
    end
  end
end
```
Run this code again and both tests are still passing! 

If you want to try adding different numbers with your new add method you can hop into an irb session in your terminal and load the file that has your calculator class, instantiate a new instance of that class storing it in a variable, then call the add method on it with the two numbers you want to add as arguments. Type exit to get out of your irb!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9i4i1828gz3dis7sregr.png)

Go ahead and try following the red/green TDD process for subtract, multiply and divide. Don't forget! You may want to test for a user trying to divide by zero and display some kind of error message.

spec/calculator_spec.rb

```ruby
require './lib/calculator.rb'

describe Calculator do 

  before(:each) do 
    @calc = Calculator.new 
  end

  context "Given two numbers" do 
    it "can add the numbers using the add method" do
      sum = @calc.add(2,3)
      expect(sum).to eql(5)
    end

    it "can add two different numbers" do 
      sum = @calc.add(8,23) 
      expect(sum).to eql(31)
    end

    it "can subtract the numbers using the subtract method" do 
      expect(@calc.subtract(6,4)).to eql(2)
    end

    it "can multiply the numbers using the multiply method" do 
      expect(@calc.multiply(3,4)).to eql(12)
    end

    it "can divide the numbers using the divide method" do 
      expect(@calc.divide(18,3)).to eql(6)
    end

    it "gives a warning if try to divide by zero" do 
      expect(@calc.divide(2,0)).to eql("You can't divide by zero!")
    end

    it "can provide the remainder when dividing two numbers using the modulo method" do 
      expect(@calc.modulo(15,5)).to eql(0)
    end

    it "can square the result of a number" do 
      expect(@calc.square(2)).to eql(4)
    end

    it "can find the squareroot of a given number" do 
      expect(@calc.squareroot(9)).to eql(3)
    end

    it "can find the factorial of a given number" do 
      expect(@calc.factorial(3)).to eql(6)
    end 
  end
end
```
lib/calculator.rb
```ruby
class Calculator

  def add(x,y)
    return x + y
  end

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

  def multiply(x,y)
    return x * y
  end

  def divide(x,y)
    if y == 0 
     return "You can't divide by zero!"
    end
    return x / y
  end

  def modulo(x,y)
    return x % y
  end

  def square(x)
    return x * x
  end

  def squareroot(x)
    return Math.sqrt(x).round()
  end

  def factorial(x)
    result = 1
    while (x > 0)
      result = result * x
      x -= 1
    end
    return result
  end

end
```
To see the file structure and folders you can look at my [github repo](https://github.com/MMcClure11/calculator). Check out [Tutorialspoint] (https://www.tutorialspoint.com/rspec/index.htm)for another good tutorial and [Relish](https://relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/before-and-after-hooks) for more on the before hook. 

I hope this is helpful for people just dipping their toes into RSpec. Any RSpec pros out there want to comment on ways to improve the tests I wrote, feedback is much appreciated!  

Happy coding!