Setting Up a Basic Unit Test with RSpec
Tags: ruby, rspec
Published: 2020-11-03 16:27:54

I have not spent a lot of time writing code in Ruby recently, but this weekend had the opportunity to develop a very simple trivia application in vanilla Ruby: two classes, a few dependencies, not much else. While this was easy enough, I wanted to add some tests to one of my classes, and I need a refresher. This post details my reintroduction to RSpec.

As background, the application took a JSON file that contained many questions, each an object with three keys: a question (value: a string), an answer (value: a string), and incorrect (value: an array of strings). I wrote a Clue class (not the most descriptive name given it includes answers, but so it goes sometimes) with one function to parse the JSON file. This looked like:

```
class Clue

    attr_accessor :question, :answer, :wrong_answers, :all_answers
    @@all = [] #an array of all the clues

    def initialize()
        @@all << self
    end
    
    def self.all 
        @@all
    end 

    def self.get_trivia
        json = JSON.parse(File.read("file_name.json"))
        json.each do |res| 
            clue = Clue.new 
            clue.question = res['question']
            clue.answer = res['correct']
            clue.wrong_answers = res['incorrect']
            clue.all_answers = [res['correct']].concat(res['incorrect'])
        end
    end 

end 
```
Having once upon a not-so-distant time spent a lot of time interacting with RSpect unit tests, I decided to use RSpec rather than Ruby'y standard library [`test-unit`](https://en.wikibooks.org/wiki/Ruby_Programming/Unit_testing).

To get started with RSpec, you need to ensure that you have included the `gem 'rspec'` in your Gemfile and have that installed correctly, then in your command line from the root direct run `rspec --init`. This will create a directory 'spec' with the file 'spec-helper.br' as well as a root file '.rspec'. 

I next created a file under spec called 'clue_test.rb' which is where all of the work happens. It's easy to forget, but you need to remember to import the class you are working with in the test file, in this case: `require 'clue'`. The next step was to test the different attributes of the Clue class. With testing, it is best to make the tests small and simple (an easy thing to do in a simple application). 

To start, I wanted to create a test `Clue` object that would be the basis for the tests. RSpec relies on the verb `describe` for a lot of its tests, and as my tests were describing the clue class, that's what we began with: `describe Clue do`. Before any tests run, a new clue was created, fed with data from a Hash. There could be other ways of doing this, but I liked simulating the logic of the JSON parsing function. 

```
require 'clue'

describe Clue do 
    before do 
        new_clue = Hash["question" => "How many folds does a chef's hat have?", "answer" => 100, "wrong_answers" => [90, 115, 50]]
        @first_clue = Clue.new
        @first_clue.question = new_clue['question']
        @first_clue.answer = new_clue['answer'] 
        @first_clue.wrong_answers = new_clue['wrong_answers']
        @first_clue.all_answers = [new_clue['answer']].concat(new_clue['wrong_answers'])
    end 

end 
```

The next step was to write a test. Go one by one. I wanted to start with some very simple. RSpec works by outlining a condition and then describing how the condition should behave. Tests generally look like something like:
```
describe "thing" do 
   it "behaves like this" do 
       expect(variable).to eq(other thing)
    end
end 
```

For the first test, I wanted to simply check if the `wrong_answers` attribute of the newly instantiated Clue was an array. This looked: 

```
    describe "wrong answers" do 
        it "includes an array of wrong answers" do 
            expect(@first_clue.wrong_answers.class).to eq(Array)
        end 
    end 
```
In the initial writing of my tests, I had actually passed the wrong_answers to the Clue as a hash. It's a small but significant error, and one that really helps you to get to know your code much better, regardless of how simple you think it. 

There are a whole host of things you can beyond checking equivalence in RSpec. One simple test I wrote checked if the `.all_answers` attribute include something from the `.wrong_answers` array:

```
    describe "all answers" do 
        it "includes wrong answers" do 
            expect(@first_clue.all_answers).to include(@first_clue.wrong_answers[0])
        end 
    end 
```

In the end, I wrote a very simple test suite, but one that got me much closer to my code. I encourage you to check out the [documentation](https://github.com/rspec/rspec-core/blob/main/README.md) and always test your code.