An in-depth look at unit testing Rails models with RSpec with TDD.
This episode covers:
- Creating a
Console
model (0:45) - Testing model validations (3:28)
- Testing model methods (12:17)
- Testing model scopes (15:12)
- Refactoring the
consoles
array of hashes into database queries (20:12)
Key code from the episode:
# db/migrate/20171111171300_create_consoles.rb
class CreateConsoles < ActiveRecord::Migration[5.1]
def change
create_table :consoles do |t|
t.string :name, null: false
t.string :manufacturer, null: false
t.timestamps
end
end
end
# app/models/console.rb
class Console < ActiveRecord::Base
validates :name, presence: true
validates :manufacturer, presence: true
scope :nintendo, -> { where(manufacturer: 'Nintendo') }
def formatted_name
"#{manufacturer} #{name}"
end
end
# spec/models/console_spec.rb
require 'rails_helper'
RSpec.describe Console do
subject { described_class.new(manufacturer: 'Nintendo', name: 'Wii') }
describe 'validations' do
describe 'name' do
it 'must be present' do
expect(subject).to be_valid
subject.name = nil
expect(subject).to_not be_valid
end
end
describe 'manufacturer' do
it 'must be present' do
expect(subject).to be_valid
subject.manufacturer = nil
expect(subject).to_not be_valid
end
end
end
describe '#formatted_name' do
it 'returns the manufacturer and name in a string' do
expect(subject.formatted_name).to eql('Nintendo Wii')
end
end
describe '.nintendo' do
it 'returns an ActiveRecord::Relation of consoles manufacturered by Nintendo' do
wii = described_class.create(manufacturer: 'Nintendo', name: 'Wii')
switch = described_class.create(manufacturer: 'Nintendo', name: 'Switch')
described_class.create(manufacturer: 'Sony', name: 'PS4')
expect(described_class.nintendo).to contain_exactly(
wii,
switch
)
end
end
end
# app/controllers/consoles_controller.rb
class ConsolesController < ApplicationController
def index
if params[:manufacturer].present?
consoles = Console.where(manufacturer: params[:manufacturer])
else
consoles = Console.all
end
render(json: { 'consoles' => consoles.map(&:formatted_name) })
end
end
# spec/requests/consoles_spec.rb
require 'rails_helper'
RSpec.describe 'Consoles requests' do
before do
Console.create(name: 'NES', manufacturer: 'Nintendo')
Console.create(name: 'SNES', manufacturer: 'Nintendo')
Console.create(name: 'Wii', manufacturer: 'Nintendo')
Console.create(name: 'Genesis', manufacturer: 'Sega')
Console.create(name: 'Xbox', manufacturer: 'Microsoft')
Console.create(name: 'Switch', manufacturer: 'Nintendo')
Console.create(name: 'PS1', manufacturer: 'Sony')
Console.create(name: 'PS2', manufacturer: 'Sony')
end
describe 'GET /consoles' do
it 'returns an array of the consoles' do
get('/consoles')
expect(response_json['consoles']).to contain_exactly(
'Nintendo NES',
'Nintendo SNES',
'Nintendo Wii',
'Sega Genesis',
'Microsoft Xbox',
'Nintendo Switch',
'Sony PS1',
'Sony PS2'
)
end
it 'supports specifying consoles for a specific manufacturer' do
get('/consoles', params: { manufacturer: 'Nintendo' })
expect(response_json['consoles']).to contain_exactly(
'Nintendo NES',
'Nintendo SNES',
'Nintendo Wii',
'Nintendo Switch'
)
end
end
end
Software used:
- Ruby 2.4.2
- RSpec 3.6.1
- Rails 5.1.4
- Mac OS
- iTerm 2
- Vim
- Tmux