RSpec(Pt. 1): Getting Started
Tags: ruby, rspec, testing
Published: 2020-08-04 07:33:05

### This post won't go in depth on writing RSpec tests, but it will show you how to configure a `.rspec` file and how to use RSpec syntax with an example for a single Ruby class at the bottom.

# Configuration

- Ruby 2.0 or higher recommended
- You must use RSpec version 3 or higher. Version 2 & 3 of RSpec are not interchangeable.
- Install RSpec with `gem install rspec`


To write a simple RSpec test for a Ruby class, you can start by `cd` into your project directory and in the terminal run `rspec --init`, which will create two things for you. 

1. A new directory named 'spec' which contains a file named `spec_helper.rb`
2. A new `.rspec` file in the root of the project directory.

`.rspec` files contain the configuration setting for testing with RSpec. You can see all of the options with `rspec --help`. 

Here are a few things you can write in the `.rspec` file to configure rspec:

1. `--no-color`, `--color` (`--color` Shows green output for passing tests, red for failing tests)
2. `--format progress`, `--format documentation` (`--format progress` will show your test examples like this in the terminal: "`....F..`" - Dots are passing, F is failed. `--format documentation` will instead of showing dots and f's, will instead show the example descriptions of your tests in a green(passing) or red(failing) color.
3. `--no-profile`, `--profile` (This one will determine whether or not RSpec shows you your fastest and slowest tests)
4. `--no-fail-fast`, `--fail-fast` (`--no-fail-fast` will run all tests, `--fail-fast` will only output the first test that fails if any)
5. `--order defined`, `--order random` (This one will either run the tests you have written in the order they are defined or at random)

If you create a `.rspec` file in your computers user directory, you can configure RSpec globally there and the rest of your projects will use that configuration.

If you use a `.rspec` file in the project directory, it will override any global `.rspec` configurations for that project.

# I have a Ruby class called `Character`, which is loading a module that I am using for another class.

```ruby
require_relative '../modules/instance_methods.rb'
class Character
    include InstanceMethods

    @@all = []

    def self.all
        @@all
    end

end
```

Here is the module:

```ruby
module InstanceMethods
        
    attr_accessor(:name, :bio, :url, :start_i, :end_i)

    def initialize(name, url)
        @name = name
        @url = url
        self.class.all << self
    end

end
```

Create a spec test for your class with the class name at the front, for example my spec file name is `character_spec.rb`. In order to test the class from the spec file, you need to load the class code. At the top of your new spec file, require the file in which you are testing.

At the top of my spec file is:
```ruby
require_relative '../lib/classes/character.rb'
``` 

# There is a syntax RSpec uses to define tests.

1) The Example Group

```ruby
describe Character do
# Example Cases go here
end
```
RSpec uses the `describe` keyword which takes a string or class name argument followed by `do` and `end` to define an Example Group. Above I used a class name, but it could also be `'Character'`.

You can have two example groups inside of each other, in which case you can either use `describe` or `context` inside of the first example group because they are the same thing.

For example, you could set the first `describe` for the Character class, and the second nested `describe` for testing the attributes of the class.

Everything between that `do` and `end` belongs to the thing we want to test. In the code above, it is a class, so we are setting the Example Group to test the Character class.

2) Example Cases
```ruby
describe Character do

   it "Instantiate with a name & URL" do
	#Expectations
   end
end
```

The keyword `it` followed by a string argument with `do` and `end` defines an Example Case. You can also use the synonym for it which is `specify`. This is where you will house the actual test, the expectation. The string is what is outputted to you when you are testing your file just so you can see which test it is.

3) Expectations
```ruby
it "Instantiate with a name & URL" do
    @character = Character.new("Luffy", "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
    expect(@character).to have_attributes(:name => "Luffy", :url => "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
end
```

This is where the actual test is. The syntax for expectations is as follows:

```ruby
expect().to()
expect().not_to()
```

`expect` will take an argument of the thing you want to test, you will call `.to()` on the end of `expect()`, then you will use something called a 'Matcher' as an argument to `.to()` to test the two. There are many matchers:

- Observation matchers (How things change)
- collection matchers (Hashes, Arrays, Strings)
- numeric-comparison matchers (numbers)
- truthiness matchers (true || false)
- Composing Matchers (Matchers than can take other matchers as its argument)
- Equivalence matchers (Ruby loose, value & object identity equality)
- Predicate Matchers (Dynamic & Custom matchers)

You can go more in depth on the abundance of them here at:
[Expectations & Matchers](https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers)

To run your spec file, run in the terminal:
`rspec` path-to-file-name.

To easily do this, type `rspec ` in the terminal with a space after it, drag and drop your spec file into the terminal and it will give the entire path to your file. 

For example:
```
rspec /Users/user/Desktop/Flatiron/Projects/CLI/spec/character_spec.rb
```

Or you can simply just run in the terminal:
```
rspec spec
```
Which will run all tests in the spec directory.

Here is my finished spec for the Character class:

```ruby
require_relative '../lib/classes/character.rb'

describe Character do

    context "attributes" do

        subject { Character.new("Luffy", "https://onepiece.fandom.com/wiki/Monkey_D._Luffy") } 

        it "Instantiate with a name & URL" do
            expect(subject).to have_attributes(:name => "Luffy", :url => "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
        end

        it "Have attributes for :bio, :start_i, :end_i" do
            expect(subject).to respond_to(:bio, :start_i, :end_i)
        end

        it ":name, :url & :bio must be strings" do
            subject.bio = "Kaizoku-ō ni ore wa naru!"
            expect(subject.name).to be_an(String)
            expect(subject.url).to be_an(String)
            expect(subject.bio).to be_an(String)
        end

        it ":start_i and :end_i must be integers" do
            subject.start_i = 2
            subject.end_i = 4
            expect(subject.start_i).to be_an(Integer)
            expect(subject.end_i).to be_an(Integer)
        end
        
    end

    context 'class Methods' do
        it '.all method which will record all instances of the class' do
            expect(Character.all).to be_an(Array)
        end
    end
    
end
```

And here is the output for when I run my spec tests:

```
Character
  attributes
    Instantiate with a name & URL
    Have attributes for :bio, :start_i, :end_i
    :name, :url & :bio must be strings
    :start_i and :end_i must be integers
  class Methods
    .all method which will record all instances of the class

Finished in 0.0073 seconds (files took 0.29535 seconds to load)
5 examples, 0 failures
```