Github Actions to run Rubocop and RSpec tests on Rails with Postgres
Tags: rails, github, docker, webdev
Published: 2020-01-27 09:29:56

Hi,

As my first post in this community, I decided to share my experience when I was testing Github's actions. I hope that you'll find it useful.

# Objectif 
My goal was to define a workflow composed of three steps for testing a rails application. These steps are:

1. Using [Rubocup](https://github.com/rubocop-hq/rubocop), the code will be statically analyzed against [the Ruby style guide](https://rubystyle.guide/).
2. If the code passes the previous step, the RSpec tests are triggered to check if the new committed code didn't break anything somewhere else.
3. Finally, if all tests pass, you will get a report generated using [SimpleCov](https://github.com/colszowka/simplecov) to see how well the tests covered the application's code

# The demo application
You can find the code that I used for the demo in this [ github repository](https://github.com/abdellani/rails-github-actions). The application is a pretty simple, one "Post" model with two attributed "title" and "content". Postgres is the database to store the data.

if you check `config/database.yml`, you'll find that I have solely edited the section related to the testing database as I didn't need to run the application in the development environment.
```yml
#config/database.yml
test:
<<: *default
host: localhost
username: postgres
database: rails-github-action_test
```

# Setting up the workflow
## Create `.github/workflows`

The first step is to create a folder called `.github/workflows` in the root of your project folder.

## Create the file that will describe the workflow
In this file, you write all the details need to run the workflow. The extension must `.yml` or `.yaml`.

### Define the environment variables
This section is not required but it makes the reconfiguration of the workflow for other contexts easier.  This section should start with the keyword `env`.
```yml
env:
  RUBY_VERSION: 2.4
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: ""
  POSTGRES_DB: postgres
```
* `RUBY_VERSION` will used to specify which version of ruby to use in the environment.
* `POSTGRES_USER`, `POSTGRES_PASSWORD` and `POSTGRES_DB` are the database related information.

### Define the name of the workflow and when it should be triggered

```yml
name: Rails tests
on: [push,pull_request]
jobs:
  job1:
   ...
  job2:
   ...
```
* `name` is the name of the workflow that will appear in the Github Actions UI.
![Workflow name](https://thepracticaldev.s3.amazonaws.com/i/ur30swy09f97aou07123.png)
* `on` specify what are the events that will trigger the workflow. In this example, it'll be triggered on `push` and `pull_request` events.
* `jobs` will contain all the `tasks` to be defined in the workflow.

### Describe jobs

#### Rubocop test
```yml
jobs:
  rubocop-test:
    name: Rubocop
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-ruby@v1
        with:
          ruby-version: ${{ env.RUBY_VERSION }}
      - name: Install Rubocop
        run: gem install rubocop
      - name: Check code
        run: rubocop
```
* `name` is the name of the task.
* `runs-on` specifies which operating system will be used to run the task. In addition to `ubuntu-18.04`, you can use `macOS` and `Windows Server 2019`. For more details, you can check this [link](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on).
* In the section `steps`, we'll define the instructions that will run in the operating system in order to prepare it, and make it run the tests. In this example :
  * We call the action [actions/checkout@v1](https://github.com/actions/checkout/tree/releases/v1) which necessary to check out the repository.
  * We call the action [actions/setup-ruby@v1](https://github.com/actions/setup-ruby) to install ruby,
  * We use `run` to run the command to install rubocop `gem install rubocop`,
  * And finally, we run the test using `rubocop`.
you can learn more about actions [here](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-actions). 

If the code passes the test, you'll get something like [this](https://github.com/abdellani/rails-github-actions/runs/369175310?check_suite_focus=true) :
![Screenshot 2](https://thepracticaldev.s3.amazonaws.com/i/ql4f6os9yv32qkwbbdvj.png)

#### RSpec tests
For the RSpect test, We use a very similar code. The first difference is that we used `needs` to precise that the task should run only after the task `rubocop-test` is finished and passed successfully. 

```yml
job:
 ...
   rspec-test:
    name: RSpec
    needs: rubocop-test
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-ruby@v1
        with:
          ruby-version: ${{ env.RUBY_VERSION }}
      - name: Install postgres client
        run: sudo apt-get install libpq-dev
      - name: Install dependencies
        run: |
          gem install bundler
          bundler install
      - name: Create database
        run: |
          bundler exec rails db:create RAILS_ENV=test
          bundler exec rails db:migrate RAILS_ENV=test
      - name: Run tests
        run: bundler exec rake
```
* As `libpq-dev` is a required package to allow the rails application to communicate with `Postgres`. We added a step to install it using `apt-get install libpq-dev`.
* To create the database and the tables, we defined the step `Create database`
* To run the test, we used the command `bundler exec rake`

At this level, if we run the tests they'll fail. The reason is that we didn't create database service yet. We need to define a new section `services` inside the `rspec-test` job.
```yml
    services:
      postgres:
        image: postgres:latest
        ports:
        - 5432:5432
```
* `image` is the docker image name.
* `port` maps the local ports to the container's ports.

```yml
 rspec-test:
    name: RSpec
    needs: rubocop-test
    runs-on: ubuntu-18.04
    services:
      postgres:
        image: postgres:latest
        ports:
        - 5432:5432
    steps:
      - uses: actions/checkout@v1
...
```

### SampleCov report
Now, we add the following step in order to upload the SimpleCov report from the testing environment and make it available to developers. 
```yml
      - name: Upload coverage results    
        uses: actions/upload-artifact@master
        if: always()
        with:
          name: coverage-report
          path: coverage
```
* we are using the action [actions/upload-artifact@master](https://github.com/actions/upload-artifact) to upload the report.
* `path` is the path of the folder/file to upload
* `name` is the name of the downloadable file.

If your code passes the tests, you'll be able to download the report from the link in the upper-right corner.
![Screenshot](https://thepracticaldev.s3.amazonaws.com/i/16j3lulx9vg1rpd47x6k.png)

You can check the full code in this [link](https://github.com/abdellani/rails-github-actions/blob/development/.github/workflows/workflow.yml).

# References
- https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions
- https://docs.knapsackpro.com/2019/how-to-run-rspec-on-github-actions-for-ruby-on-rails-app-using-parallel-jobs
- https://jer-k.github.io/github-action-rspec-simplecov/