Last active
February 2, 2017 08:00
-
-
Save raviwu/dfb562b7eab40540724db5f0e0e2d48b to your computer and use it in GitHub Desktop.
RSpec Patterns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) { '[email protected]' } | |
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