Chapter 12

Alexandria: Setup Time

Time to start writing some code! Without further ado, let’s build an awesome API with Rails 5.

12.1. Setting up our environment

First thing we need to do is install the rails gem. You should already have Ruby and Rubygems installed.

Ensure you’re using Ruby 2.5.0 with the mrwa gemset we created in the first chapter.

rvm gemset use 2.5.0@mrwa

Open a terminal and run the following command to install Rails 5.2 without any kind of documentation.

gem install rails --no-ri --no-rdoc --version="5.2.0"

We don’t need to download the documentation since we will be using the live version if we have any problem. We will also use a different testing framework than the one coming with Rails by default.

12.2. Creating the application

Next, we are going to create the application. Navigate to the folder we created in the introduction (master_ruby_web_apis), create a new folder named module_02, and run the command below from inside to generate a Rails 5 API-only application.

rails new alexandria --api --skip-test-unit

–api specifies that we want to use the API mode. This will tell the Rails generators not to include anything unnecessary to build an API.

Move inside the created folder with cd.

cd alexandria

If it was generated and you’re using RVM, open .ruby-version at the root of the application and change the content from:

2.5.0

to:

2.5.0@mrwa

With this, our gemset will automatically be loaded.

Before we add any code, let’s give it a try and start the server.

rails server

Output

=> Booting Puma
=> Rails 5.x.x application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.4.0 (ruby 2.5.0), codename: Owl Bowl Brawl
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000

Open a web browser, head to http://localhost:3000 and you should see the Figure 1 popping on your screen.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/12/rails_new
Figure 1

Congratulations!

12.3. Git

Now that we have a basic application, we are going to use Git to version it. Since we will also be deploying this application at the end of the module, let’s create a repository for it on GitHub. Setting up Continuous Integration and Delivery later on will allow us to get the code deployed simply by pushing to GitHub.

Git is a great tool that I’m hoping you are already using. If not, let me give you a quick introduction. From the Git website:

“Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.”

Basically, Git allows you to version your code and collaborate with other people on it. The best part is that it makes it very simple to do those two things. You will see how great Git is as we will be using it throughout this module for Alexandria.

Box 12.1. GUI for Git

There are many tools with a nice graphic interface to manage Git repositories. I personally like GitX because it’s pretty minimalistic; I only use it to stage changes and commit. For everything else, I use the command line because it gives me more control.

I know some people are huge fans of SourceTree, so give it a try if you’re looking for a tool to handle the entire Git flow in a graphical way.

If you already have your favorite, feel free to use it. However, the instructions in this book will be oriented towards running Git in a terminal to learn its various commands.

12.3.1. Installing Git

On Mac OS X

If you’ve installed Homebrew (as recommended in the previous chapters), you can just do the following.

brew install git

If you don’t want to use Homebrew (why not?), checkout this link to install Git.

On Linux

Just use your packet manager to install Git. For Debian-based distribution, use the following command:

sudo apt-get install git

12.3.2. The Initial Commit

The first thing we need to do is tell Git that we want to version the current project. Inside master_ruby_web_apis/alexandria, run the following command.

git init

Then we are going to stage all the files as ready to be committed.

git add .

Finally, we can commit the staged changes.

git commit -m "Initial Commit"

We’ll learn more about Git along the way.

12.3.3. Creating the GitHub repository

Sign Up if you don’t already have an account

Simply head to GitHub and sign up. Once you’ve confirmed your account, you will be able to create repositories. See Figure 2.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/12/github_signup
Figure 2: GitHub’s signup page.

Create a new repository

Once you’re logged in, create a new repository. See Figure 3 for reference. Name the repository (I called mine Alexandria) and add a quick description. Finally, click on Create Repository.

Note that if you have a free account, you can only create Public repositories which are enough to follow this book. If you are a student, you can have unlimited repositories and a bunch of other cool stuff.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/12/github_new_repo
Figure 3

Get the repository URL

On the following page, you should see a list of steps as shown in Figure 4. Take note of the URL of your repository, we are going to need it. Mine is https://github.com/T-Dnzt/alexandria.git for example.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/12/remote
Figure 4

12.3.4. Linking our local repository to GitHub

We now have a GitHub repository and a local Git project, so we just have to link them together. To do this, we are going to add a remote repository to our local version using the command below.

git remote add origin YOUR-GITHUB-REPO-URL

Replace YOUR-GITHUB-REPO-URL with the actual URL you got in the previous section. For my repository, that would be:

git remote add origin https://github.com/T-Dnzt/alexandria.git

Finally, we can push all our local changes to the remote repository on GitHub using the git push command.

git push origin master

Access https://github.com/T-Dnzt/YOUR-GITHUB-REPO-NAME and you should see the list of all the files in our Rails project. See Figure 5 for reference.

https://s3.amazonaws.com/devblast-mrwa-book/images/figures/12/github_files
Figure 5

12.4. Testing Environment

We’ve got a working Rails application available on a remote Git repository. Now we can move on to the next topic - automated tests!

12.4.1. Why Write Automated Tests?

Writing tests is mandatory in good development teams. You can be the smartest developer in the world but your code will still have bugs. The important thing is catching those bugs as soon as possible, which is usually before they leave your computer.

Automated tests have a huge amount of benefits.

  • Prevent regressions

You fixed that annoying bug, but you didn’t write a test for it. Two weeks later, the problem is back because another developer has re-introduced it. Sound familiar? This could have been avoided by first writing a failing test demonstrating the issue to not only identify where the problem lies, but also to document it. The bug can then be fixed and the test will pass. If someone breaks it in the future, the test will start failing again and the regression won’t make it to production.

  • Reduce the number of bugs

When you write automated tests, you identify different scenarios that could occur. Some of these scenarios will trigger unexpected behaviors in your code. The only way to find them is to either test the code manually or write automated tests. You definitely don’t want those scenarios to happen once the application is in production.

  • Simplified Refactoring

Anytime you change code, there is a risk that you will break something. Refactoring is very important but comes with the same risks as changing anything. Having automated tests checking the output of what you’re refactoring will let you make changes safely and with more confidence, leading to an improved code quality.

  • Ensure the Code is Doing What Is Expected

A lot of times, a piece of code is doing less or more than it should. Writing tests first, as well as following a TDD or BDD approach, lets you focus on writing the minimum amount of code necessary to make the test pass. This leads to simpler and smaller code.

  • Make It Easier to Integrate People Into the Team

When a new developer joins your team, you might constantly be afraid he is going to break something. If you’re not afraid, you should be. Changing code that you aren’t familiar with is hard and usually creates issues. To counter that, a strong testing suite is the best solution. With this tool in hand, newcomers can change things safely and will know anytime they break something.

  • Less (or No) manual Testing

Who wants to do manual testing, especially for a web API?. When we did it in the first module, and it was such a pain. Luckily, the application was small that we survived. For bigger projects, a testing suite can save a huge amount of time and prevents the need to manually test the new code. You should always do a bit of manual testing, (after all, tests can have bugs too), but once a feature and its automated tests are working there is no need to go back and test it.

  • Allow You to Relax and Sleep at Night

Who wouldn’t want that? Since I’ve started writing tests for every piece of code I write, I’m more confident and way less stressed when deploying to production. I also sleep better at night and I’ve heard that it’s the same for my fellow devops engineers.

12.4.2. Different types of tests

A Rails application is composed of many entities: models, controllers, views, serializers, presenters, and everything in between. Testing them requires writing tests in different styles.

Unit Tests

Unit Tests can be used to test very tiny parts of your system in isolation. Those tests ensure that those parts implement the expected behavior without considering the whole project. Unit Tests are fast because they don’t need the whole system to run. One class and one method is enough to write a unit test.

In Rails, the tests we will write for models are unit tests for example.

Integration Tests

Unit Tests are great to test things in isolation. But in the real world, all those things need to work together properly. That’s when integration tests come into play. Since they rely on more components and test systems at a higher level, they tend to be slower and bigger than unit tests.

Request tests in Rails are integration tests.

Hybrid Tests

Unit tests are on one extreme side and integration tests are on the other. In between, there are all the other tests that don’t really fall into one of these categories. They don’t test the whole system or individual components - instead, they test a few components together, like controller & model, just like in Rails controller tests.

What about Alexandria?

We will use unit tests to tests our Rails models and our custom Ruby classes but we won’t write controller tests.

Controller tests are unit tests focusing on testing the controllers in isolation, as simple Ruby classes and with all the required stubbing. I personally prefer to write request tests to test resources since they go through the whole Rails stack. That way, it’s simpler to write tests from a client’s perspective. It is easier and faster than ever to use request tests instead of controller tests and Rails 5 seems to be pushing towards that as well.

Since request tests are basically integration tests, our testing suite will be slower but that’s a drawback I’m ready to accept for Alexandria.

12.4.3. Setting Up The Tools

To test our Rails API, we will be using five different tools. Here is the list with quick descriptions. Some of those descriptions come directly from their documentation.

RSpec is a testing framework for Ruby that makes “TDD Production and Fun.” RSpec will be our main tool to write tests and will be incredibly useful to ensure that we write code that works.

factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.”

“Database Cleaner is a set of strategies for cleaning your database in Ruby.

The original use case was to ensure a clean state during tests. Each strategy is a small amount of code, but is code that is usually needed in any Ruby app that is testing with a database.”

Webmock is a “library for stubbing and setting expectations on HTTP requests in Ruby.” We will use Webmock to ensure that our Rails endpoints work as expected.

“Shoulda Matchers provides RSpec- and Minitest-compatible one-liners that test common Rails functionality. These tests would otherwise be much longer, more complex, and error-prone.”

Alright, we have the list of tools we need. Now we can add them to our Gemfile to get them loaded in our application.

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.0'

gem 'rails', '5.2.0'
gem 'sqlite3'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end

group :development do
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  gem 'shoulda-matchers'
  gem 'webmock'
  gem 'database_cleaner'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

A quick bundle install will get everything in place.

bundle install

Everything should be installed now. First, we’re going to use RSpec generator to create the basic testing configuration we need to continue.

rails generate rspec:install

Output

Running via Spring preloader in process 32843
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

This generator just created 1 folder and 3 files:

  • .rspec: This file contains the configuration for RSpec - like how we want our tests to be colored, for example.
  • spec/: This folder will contain all our tests.
  • spec/spec_helper: The spec_helper file contains the basic configuration required to run tests that don’t rely on Rails.
  • spec/rails_helper: The rails_helper, as the name suggests, is just like the spec_helper file but for all tests that depend on the Rails stack.

In order to use all the gems we added with RSpec, we need to add a bit of configuration to the rails_helper file.

  • Require all the testing tools
  • Specify the use of FactoryBot
  • Add the cleaning strategy for DatabaseCleaner
  • Configure Shoulda-Matchers

See the file below for the changes. You should be able to copy/paste this code once you’ve understood it.

# spec/rails_helper.rb
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?

# Require RSpec for Rails, webmock and database_cleaner
require 'rspec/rails'
require 'webmock/rspec'
require 'database_cleaner'

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  puts e.to_s.strip
  exit 1
end

RSpec.configure do |config|
  config.use_transactional_fixtures = true
  config.infer_spec_type_from_file_location!
  config.filter_rails_from_backtrace!

  # Load Factory Girl methods
  config.include FactoryBot::Syntax::Methods

  # Configure Database Cleaner
  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

  # Configure Shoulda-Matchers
  Shoulda::Matchers.configure do |config|
    config.integrate do |with|
      with.test_framework :rspec
      with.library :rails
    end
  end
end

To ensure that we got everything right, we can run the rspec command from within our project. The expected output is presented below.

rspec

Output

Finished in 0.00028 seconds (files took 0.04333 seconds to load)
0 examples, 0 failures

12.5. Our First Test

Everything is ready for us to start writing code. We are going to use the RED-GREEN-REFACTOR cycle to write our first tests. But what are we going to test?

For starters, our API is going to need a model to represent publishers - we can start with that. But before we write any test, let’s begin by creating the Publisher model and the associated table in the database.

12.5.1. The Publisher Model

The publishers table will have 4 columns: id, name, created_at and updated_at. Since Rails takes care of the id, created_at and updated_at columns, we can generate our model with the following command:

rails g model Publisher name:string

As you can see below or in your terminal, this command creates the model file, the migration, the test file in spec/ and the factory file in spec/factories.

Running via Spring preloader in process 3854
      invoke  active_record
      create    db/migrate/TIMESTAMP_create_publishers.rb
      create    app/models/publisher.rb
      invoke    rspec
      create      spec/models/publisher_spec.rb
      invoke      factory_bot
      create        spec/factories/publishers.rb

Let’s open the migration file first, located at:

db/migrate/TIMESTAMP_create_publishers.rb.

We don’t have anything to change here, but we can see that Rails has generated the migration we were expecting and added the name attribute as a string.

# db/migrate/TIMESTAMP_create_publishers.rb
class CreatePublishers < ActiveRecord::Migration[5.2]
  def change
    create_table :publishers do |t|
      t.string :name

      t.timestamps
    end
  end
end

Let’s run this new migration in the development and test environments.

rails db:migrate && rails db:migrate RAILS_ENV=test

In Rails 5, all rake commands, like rake db:migrate for example, have been aliased with rails. That means any rake command can now be replaced with rails for simplicity.

Now let’s take a look at the Publisher model located at app/models/publisher.rb. Pretty empty, huh? That’s fine for now - we’ll write tests before we add anything there.

# app/models/publisher.rb
class Publisher < ApplicationRecord
end

The generator has also created a factory for us. Open it (spec/factories/publishers.rb) to have a look. Replace the default name with the name of a real publisher, like “O’Reilly”. This factory will be used to create instances of the Publisher model with the values defined in this file.

# spec/factories/publishers.rb
FactoryBot.define do
  factory :publisher do
    name { "O'Reilly" }
  end
end

Finally, we can see what’s inside the test file that was generated under spec/models/ for our Publisher model.

# spec/models/publisher_spec.rb
require 'rails_helper'

RSpec.describe Publisher, :type => :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

There is nothing interesting there yet, but we are going to change that right now. Following the RED-GREEN-REFACTOR cycle, we will first write a test for a feature we want in Alexandria. The first thing we need is a way to validate that a publisher always has a name.

12.5.2. RSpec Vocabulary

Before we write our first test, there are a bunch of methods we need to understand. Most of these make it easier to organize and manage your code by grouping them. Let’s go through them.

  • describe is used to define the target for a set of tests. describe '#valid?' do ... end means that we are writing tests for the valid? method.
  • context allows us to define specific situations and write tests for each one of them. Take these two contexts for example:
context 'with valid params' do .. end
context 'with invalid params' do ... end

With those contexts, we can easily group tests that share the same context instead of naming our tests like it does something when given valid params.

  • it is used to define a test. It takes one parameter, the name of the test, and a block which contains the expectations.
  • expect is a method used to define what the expected output is for a given input. You will understand how expect works with all the tests we are going to create.
Box 12.2. "Should" You or Not?

Originally, it was recommend to name your test using the should verb as in it 'should work'. These days, the best practice is to simply use the verb representing the expected behavior like it 'works'.

12.5.3. Validation Test

To do this, we can use one of Shoulda-Matchers matchers:

validate_presence_of

The whole test would be a one-liner as you can see below:

it { should validate_presence_of(:name) }

Using Shoulda-Matchers allows us to write this kind of thing instead of the longer version, which can be seen below.

# We are not using Factory Girl for simplicity in those examples.
it "is invalid with a 'nil' name" do
  publisher = Publisher.new(name: nil)
  expect(publisher.valid?).to be false
end

it 'is invalid with a blank name' do
  publisher = Publisher.new(name: '')
  expect(publisher.valid?).to be false
end

it 'valid with a name' do
  publisher = Publisher.new(name: "O'Reilly")
  expect(publisher.valid?).to be true
end

Let’s add the one-liner we just wrote to the publisher_spec file. We will also add another test that will be present for each one of our models. Since we will be using the factory for all our future tests, it is important to check that this factory allows us to create valid records.

# spec/models/publisher_spec.rb
require 'rails_helper'

RSpec.describe Publisher, :type => :model do
  it { should validate_presence_of(:name) }

  it 'has a valid factory' do
   expect(build(:publisher)).to be_valid
  end
end

Now we can start running our tests! Let’s just run the tests in the publisher_spec file, since we don’t have any other tests anyway…

rspec spec/models/publisher_spec.rb

Failure (RED)

Failure/Error: it { should validate_presence_of(:name) }
   Publisher did not properly validate that :name cannot be empty/falsy.
     After setting :name to ‹nil›, the matcher expected the Publisher to be
     invalid, but it was valid instead.

Finished in 0.05762 seconds (files took 1.82 seconds to load)
2 example, 1 failure

Our test clearly failed. That’s the first part of the cycle - now it’s time to fix it. In order to do that, we can simply add a validation to the Publisher model. With validates: name, presence: true added, our test should pass.

# app/models/publisher.rb
class Publisher < ApplicationRecord
  validates :name, presence: true
end

Give it another try.

rspec spec/models/publisher_spec.rb

Success (GREEN)

.

Finished in 0.05325 seconds (files took 1.8 seconds to load)
2 example, 0 failures

Great, it’s working now! However, the default output for successful tests doesn’t say much except “.”. We can change that by editing the .rspec file and adding the –format documentation option.

--color
--require spec_helper
--format documentation

If we run the tests again, we now have all the details.

rspec spec/models/publisher_spec.rb

Success (GREEN)

Publisher
  should validate that :name cannot be empty/falsy
  has a valid factory

Finished in 0.05501 seconds (files took 1.94 seconds to load)
2 examples, 0 failures

For the rest of this module, I will only put the last line of the output to keep things compact, but I recommend using the documentation format.

Time to commit and push our changes to GitHub!

12.6. Pushing Our Changes

First, we need to know all the changes that have been made. We might want to be picky about what gets pushed.

git status

Output

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   Gemfile
	modified:   Gemfile.lock

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.rspec
	app/models/publisher.rb
	db/migrate/
	db/schema.rb
	spec/

no changes added to commit (use "git add" and/or "git commit -a")

It seems everything can simply be added.

git add .

Now, let’s see if all the files were correctly staged.

git status

Output

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   .rspec
	modified:   Gemfile
	modified:   Gemfile.lock
	new file:   app/models/publisher.rb
	new file:   db/migrate/20160601060927_create_publishers.rb
	new file:   db/schema.rb
	new file:   spec/factories/publishers.rb
	new file:   spec/models/publisher_spec.rb
	new file:   spec/rails_helper.rb
	new file:   spec/spec_helper.rb

Commit with a commit message describing what we’ve done.

git commit -m "Setup testing environment"

Output

[master 24b96fe] Setup testing environment
 10 files changed, 241 insertions(+), 37 deletions(-)
 create mode 100644 .rspec
 rewrite Gemfile (77%)
 create mode 100644 app/models/publisher.rb
 create mode 100644 db/migrate/20160601060927_create_publishers.rb
 create mode 100644 db/schema.rb
 create mode 100644 spec/factories/publishers.rb
 create mode 100644 spec/models/publisher_spec.rb
 create mode 100644 spec/rails_helper.rb
 create mode 100644 spec/spec_helper.rb

And push the committed changes to GitHub.

git push origin master

Output

Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (17/17), done.
Writing objects: 100% (19/19), 5.05 KiB | 0 bytes/s, done.
Total 19 (delta 4), reused 0 (delta 0)
To https://github.com/T-Dnzt/alexandria.git
   7dfa1a6..24b96fe  master -> master

12.7. Wrap Up

In this chapter, we’ve learned a lot! We created Alexandria, set up Git and took a deep dive into automated testing. We are now ready to proceed with creating models and writing more tests!