Skip to content

Instantly share code, notes, and snippets.

@raviwu
Last active February 2, 2017 08:00
Show Gist options
  • Save raviwu/dfb562b7eab40540724db5f0e0e2d48b to your computer and use it in GitHub Desktop.
Save raviwu/dfb562b7eab40540724db5f0e0e2d48b to your computer and use it in GitHub Desktop.
RSpec Patterns
# app/models/user.rb
class User < ActiveRecord::Base
has_secure_password
validates :firstname, presence: true, length: 4..20
validates :middlename, length: 4..20, allow_blank: true
validates :lastname, presence: true, length: 4..20
validates :email, format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i }
validates :password, presence: true, confirmation: true, length: { minimum: 8 }
# ...
def fullname
[firstname, middlename, lastname].compact.join(' ')
end
end
# ======= Minimum Valid Object =======
# Workflow:
# 1. Build the Ojbect
# 2. Assign valid attributes
# 3. Assert the valid state
# 4. Mutate the attribute that expected to be invalid
describe User do
subject(:user) do
# 1. Build the Ojbect
described_class.new(
firstname: firstname,
middlename: middlename,
lastname: lastname,
email: email,
password: password
)
end
# 2. Assign valid attributes
let(:firstname) { 'Ravi' }
let(:middlename) { nil }
let(:lastname) { 'Wurzmann' }
let(:email) { 'raviwu@gmail.com' }
let(:password) { 'Passw0rd!' }
# 3. Assert the valid state
it { is_expected.to be_valid }
context 'with a firstname that is over 20 chars' do
# 4. Mutate the attribute that expected to be invalid
let(:firstname) { 'a very very very very long long long long long long long firstname' }
it { is_expected.to be_invalid }
end
context 'with a firstname that is less than 4 chars' do
let(:firstname) { 'srt' }
it { is_expected.to be_invalid }
end
end
# ======= Permutation Table =======
# Workflow:
# 1. Define sets of data
# 2. Define the output of each set
# 3. Assert the method creates the output from the data (input/output)
describe User do
# ... continue previous setup for valid subject
describe '#fullname' do
shared_examples_for 'a fullname' do|(first, middle, last), output|
subject(:fullname) { user.fullname }
let(:firstname) { first }
let(:middlename) { middle }
let(:lastname) { last }
it { is_expected.to eq output }
end
{
['Ravi', 'LW', 'Wurzmann'] => 'Ravi LW Wurzmann',
[nil, 'LW', 'Wurzmann'] => 'LW Wurzmann',
['Ravi', nil, 'Wurzmann'] => 'Ravi Wurzmann',
['Ravi', 'LW', nil] => 'Ravi LW',
[nil, nil, 'Wurzmann'] => 'Wurzmann',
[nil, 'LW', nil] => 'LW',
['Ravi', nil, nil] => 'Ravi',
[nil, nil, nil] => ''
}.each do |name_set, output|
it_behaves_like 'a fullname', name_set, output
end
end
end
# ======= Golden Master =======
# This is not a good practice for large scale testing, this kind of manually approval should apply on limited and isolated tests
# Purpose:
# 1. Backfilling untested legacy code
# 2. Uncertain expectations require visual confirmation
# 3. Code complexity significantly exceeds current domain knowledge
# Workflow:
# 1. Take a snapshot of an object (to a file)
# 2. Verify the snapshot (manually)
# 3. Compare future versions to the verified 'master'
# https://github.com/kytrinyx/approvals
# spec/spec_helper.rb
require 'approvals/rspec'
# path/to/test_spec.rb
it 'works' do
verify format: :html do
"<html><head></head><body><h1>Ruby on Rails</h1></body></html>"
end
end
# Manually verify snapshots
$ cd /path/to/app
$ approvals verify
# ======= Suggested Practice =======
# use `let(:user)` instead of instance variables `@user`
# descriptive naming
it { expect(user).to be_valid }
it { expect(user_with_duplicated_email).to be_invalid }
# better than...
it { expect(user1).to be_valid }
it { expect(user2).to be_invalid }
# extract common expectations
# custom mather might help if same expectation logic is repeatly occurs
# factories over fixtures
# https://github.com/thoughtbot/factory_girl
# `FactoryGirl.lint!` can run all the factory setup and tells if the setup are valid, hence
# `FactoryGirl.lint!` can be treated as a MVO instead of building from `Class.new`
describe User do
subect(:user) { FactoryGirl.build :user }
end
# After Readings:
# http://randycoulman.com/blog/categories/getting-testy/
# http://betterspecs.org/
# http://www.poodr.com/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment