Share
Embed
#RailsPacific - Taming Chaotic Specs - RSpec Design Patterns
Don’t you hate when testing takes 3x as long because your specs are hard to understand? Following a few simple patterns, you can easily take a bloated spec and make it DRY and simple to extend. We will take a bloated sample spec and refactor it to something manageable, readable and concise.
## With Presenter Notes ##
https://speakerdeck.com/acuppy/number-railspacific-notes-taming-chaotic-specs-rspec-design-patterns
Adam Cuppy (he/him)
May 20, 2016
Transcript
139 slides
-
1. Communicate expectations 2. Encourage consistency 3. Reduce the mental
load Design Patterns… -
1. Start with a “valid” object 2. Make one specific
change 3. Assert the “valid” object is now “invalid” M.V.O. Workflow… -
# 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([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i } validates :password, presence: true, confirmation: true, length: { minimum: 8 } # ... end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# 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([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i } validates :password, presence: true, confirmation: true, length: { minimum: 8 } # ... end -
# 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([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i } validates :password, presence: true, confirmation: true, length: { minimum: 8 } # ... end -
# 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([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i } validates :password, presence: true, confirmation: true, length: { minimum: 8 } # ... end Other validations failing!! -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
describe User do subject(:user) { described_class.new } context 'with a
firstname that is over 20 chars' do # ... end context 'with a firstname that is under 4 chars' do # ... end end -
describe User do subject(:user) { described_class.new } context 'with a
firstname that is over 20 chars' do # ... end context 'with a firstname that is under 4 chars' do # ... end end Too Long -
describe User do subject(:user) { described_class.new } context 'with a
firstname that is over 20 chars' do # ... end context 'with a firstname that is under 4 chars' do # ... end end Too Short -
describe User do subject(:user) { described_class.new } context 'with a
firstname that is over 20 chars' do # ... end context 'with a firstname that is under 4 chars' do # ... end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } # ... end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } # ... end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } # ... end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } # ... end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
# spec/models/user_spec.rb describe User do it "should be invalid" do
user = User.new(firstname: "really really ... super long firstname") expect(user.valid?).to_not eq true user = User.new(firstname: "sht") expect(user.valid?).to_not eq true end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } # ... end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } # ... end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end Fix false positive ??? -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } context
'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end ?? -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end ?? ?? -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end Fix false positive ??? -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } specify { expect(user).to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } specify { expect(user).to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } specify { expect(user).to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname } let(:firstname)
{ 'Adam' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end context 'with a firstname that is under 4 chars' do let(:firstname) { 'srt' } it { is_expected.to be_invalid } end end -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end #1: Build the subject -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end #2: Set the config values -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end #3 Assert a Valid state -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end #4 Mutate -
describe User do subject(:user) { described_class.new firstname: firstname, middlename: middlename,
lastname: lastname, email: email, password: password } let(:firstname) { 'Adam' } let(:middlename) { nil } let(:lastname) { 'Cuppy' } let(:email) { '[email protected]' } let(:password) { 'Passw0rd!' } it { is_expected.to be_valid } context 'with a firstname that is over 20 chars' do let(:firstname) { 'really really ... super long firstname' } it { is_expected.to be_invalid } end # ... end -
1. Define sets of data 2. Define the output of
each set 3. Assert the method creates the output from the data (input/output) P.T. Workflow… -
class User < ActiveRecord::Base # ... def fullname [firstname, middlename,
lastname].compact.join ' ' end end -
class User < ActiveRecord::Base # ... def fullname [firstname, middlename,
lastname].compact.join ' ' end end -
class User < ActiveRecord::Base # ... def fullname [firstname, middlename,
lastname].compact.join ' ' end end -
class User < ActiveRecord::Base # ... def fullname [firstname, middlename,
lastname].compact.join ' ' end end -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end Anything missing ?? -
describe User do # ... describe ‘#fullname' do subject(:fullname) {
user.fullname } context 'when firstname is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end context 'when lastname is blank' do let(:lastname) { nil } it { is_expected.to eq firstname } end context 'when first and lastname are blank' do let(:firstname) { nil } let(:lastname) { nil } it { is_expected.to be_blank } end context 'when there are all three names' do let(:middlename) { 'Jimmy' } it { is_expected.to eq "#{firstname} #{middlename} #{lastname}" } end end # ... end -
describe ‘#fullname' do subject(:fullname) { user.fullname } context 'when firstname
is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end #... end -
describe ‘#fullname' do subject(:fullname) { user.fullname } context 'when firstname
is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end #... end -
describe ‘#fullname' do subject(:fullname) { user.fullname } context 'when firstname
is blank' do let(:firstname) { nil } it { is_expected.to eq lastname } end #... end -
describe ‘#fullname' do # context 'when firstname is blank' do
# let(:firstname) { nil } # it { is_expected.to eq lastname } # end { [nil, 'James', 'Johnson'] => 'James Johnson', }.each do |name_set, output| # ... end end Firstname -
describe ‘#fullname' do # context 'when firstname is blank' do
# let(:firstname) { nil } # it { is_expected.to eq lastname } # end { [nil, 'James', 'Johnson'] => 'James Johnson', }.each do |name_set, output| # ... end end middlename -
describe ‘#fullname' do # context 'when firstname is blank' do
# let(:firstname) { nil } # it { is_expected.to eq lastname } # end { [nil, 'James', 'Johnson'] => 'James Johnson', }.each do |name_set, output| # ... end end Lastname -
describe ‘#fullname' do # context 'when firstname is blank' do
# let(:firstname) { nil } # it { is_expected.to eq lastname } # end { [nil, 'James', 'Johnson'] => 'James Johnson', }.each do |name_set, output| # ... end end Fullname -
describe ‘#fullname' do # ... { [nil, 'James', 'Johnson'] =>
'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' }.each do |name_set, output| # ... end end -
describe ‘#fullname' do # ... { [nil, 'James', 'Johnson'] =>
'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' }.each do |name_set, output| # ... end end Anything missing ?? -
describe ‘#fullname' do # ... { [nil, 'James', 'Johnson'] =>
'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' ['Jimmy', nil, nil] => 'Jimmy', [nil, nil, 'Johnson'] => 'Johnson', ['Jimmy', nil, 'Johnson'] => 'Jimmy Johnson' }.each do |name_set, output| # ... end end -
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 { [nil, 'James', 'Johnson'] => 'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' ['Jimmy', nil, nil] => 'Jimmy', [nil, nil, 'Johnson'] => 'Johnson', ['Jimmy', nil, 'Johnson'] => 'Jimmy Johnson' }.each do |name_set, output| it_behaves_like 'a fullname', name_set, output end end -
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 { [nil, 'James', 'Johnson'] => 'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' ['Jimmy', nil, nil] => 'Jimmy', [nil, nil, 'Johnson'] => 'Johnson', ['Jimmy', nil, 'Johnson'] => 'Jimmy Johnson' }.each do |name_set, output| it_behaves_like 'a fullname', name_set, output end end -
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 { [nil, 'James', 'Johnson'] => 'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' ['Jimmy', nil, nil] => 'Jimmy', [nil, nil, 'Johnson'] => 'Johnson', ['Jimmy', nil, 'Johnson'] => 'Jimmy Johnson' }.each do |name_set, output| it_behaves_like 'a fullname', name_set, output end end -
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 { [nil, 'James', 'Johnson'] => 'James Johnson', [nil, 'James', nil] => 'James', ['Jimmy', 'James', nil] => 'Jimmy James', ['Jimmy', 'James', 'Johnson'] => 'Jimmy James Johnson' ['Jimmy', nil, nil] => 'Jimmy', [nil, nil, 'Johnson'] => 'Johnson', ['Jimmy', nil, 'Johnson'] => 'Jimmy Johnson' }.each do |name_set, output| it_behaves_like 'a fullname', name_set, output end end -
1. Backfilling untested legacy code 2. Uncertain expectations require visual
confirmation 3. Code complexity significantly exceeds current domain knowledge Golden Master Testing -
require 'rails_helper' RSpec.describe User, type: :model do pending "add some
examples to (or delete) #{__FILE__}" end -
1. Take a snapshot of an object (to a file)
2. Verify the snapshot (manually) 3. Compare future versions to the verified “master” Golden Master Testing Workflow… -
# spec/spec_helper.rb require ‘approvals/rspec' # path/to/spec.rb it 'works' do verify
format: :html do "<html><head></head><body><h1>ZOMG</h1></body></html>" end end # Manually verify snapshots $ cd /path/to/app $ approvals verify https://github.com/kytrinyx/approvals -
# spec/spec_helper.rb require ‘approvals/rspec' # path/to/spec.rb it 'works' do verify
format: :html do "<html><head></head><body><h1>ZOMG</h1></body></html>" end end # Manually verify snapshots $ cd /path/to/app $ approvals verify https://github.com/kytrinyx/approvals -
# spec/spec_helper.rb require ‘approvals/rspec' # path/to/spec.rb it 'works' do verify
format: :html do "<html><head></head><body><h1>ZOMG</h1></body></html>" end end # Manually verify snapshots $ cd /path/to/app $ approvals verify https://github.com/kytrinyx/approvals -
Best Practices Better Good, but not Required Other ways of
doing things, and why it might be helpfuL…ideas -
Best Practices Better Good, but not Required Other ways of
doing things, and why it might be helpfuL…ideas -
@fullname = 'Adam Cuppy' specify { expect(@fullnmae).to eq 'Adam Cuppy'
} # => expected: "Adam Cuppy", got: nil ??? -
let(:fullname) { 'Adam Cuppy' } specify { expect(fullnmae).to eq 'Adam
Cuppy' } # => NameError: undefined local variable or method `fullnmae' -
describe 'validations' do subject(:user) { User.new email: '[email protected]' } it
{ is_expected.to be_valid } context 'when an email is used' do let(:user_with_duplicate_email) do User.new email: user.email end it { expect(user_with_duplicate_email).to be_invalid } end end -
describe 'validations' do subject(:user) { User.new email: '[email protected]' } it
{ is_expected.to be_valid } context 'when an email is used' do let(:user_with_duplicate_email) do User.new email: user.email end it { expect(user_with_duplicate_email).to be_invalid } end end -
describe 'validations' do subject(:user) { User.new email: '[email protected]' } it
{ is_expected.to be_valid } context 'when an email is used' do let(:user_with_duplicate_email) do User.new email: user.email end it { expect(user_with_duplicate_email).to be_invalid } end end -
describe 'validations' do subject(:user) { User.new email: '[email protected]' } it
{ is_expected.to be_valid } context 'when an email is used' do let(:user_with_duplicate_email) do User.new email: user.email end it { expect(user_with_duplicate_email).to be_invalid } end end -
require 'rspec/expectations' RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual
% expected == 0 end end RSpec.describe 9 do it { is_expected.to be_a_multiple_of(3) } end https://www.relishapp.com/rspec/rspec-expectations/v/3-3/docs/custom-matchers/define-matcher -
require 'rspec/expectations' RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual
% expected == 0 end end RSpec.describe 9 do it { is_expected.to be_a_multiple_of(3) } end https://www.relishapp.com/rspec/rspec-expectations/v/3-3/docs/custom-matchers/define-matcher