spec/shared_example_specs.rb
# frozen_string_literal: true
# Note: file cannot be renamed to shared_spec because of rspec bug that double loads
# shared spec files ending in _spec.rb https://github.com/rspec/rspec-core/issues/82
# Tests timestamps existence - common to multiple classes
RSpec.shared_examples 'timestamps?' do
context 'timestamps' do
it { expect(responds(:updated_at)).to be_truthy }
it { expect(responds(:created_at)).to be_truthy }
end
end
# Tests that a class does not respond to a missing attribute
RSpec.shared_examples 'missing attribute' do
context 'missing attribute' do
it { expect(subject.respond_to?(:not_attribute)).not_to be_truthy }
end
end
# Tests common to multiple classes - existence of timestamps
# and non-existence of missing attributes
RSpec.shared_examples 'common attributes' do
include_examples 'timestamps?'
include_examples 'missing attribute'
end
# Tests that class has discard attribute
RSpec.shared_examples 'discard attribute' do
context 'discarded' do
it { expect(responds(:discarded_at)).to be_truthy }
end
end
# Tests if an object responds to method
RSpec.shared_examples 'responds' do |attribute|
context "##{attribute}" do
it { expect(responds(attribute)).to be_truthy }
end
end
RSpec.shared_examples 'respond' do |attributes|
attributes.each { |a| include_examples 'responds', a }
end
# Tests if an object is invalid with default constructor
RSpec.shared_examples 'invalid create' do |text|
describe '.create!' do
context text do
it { expect { described_class.create! }.to raise_error ActiveRecord::RecordInvalid }
end
end
end
# Tests object creation -
# with missing name
# 1 character name
# longer than 1 character
# duplicate names
RSpec.shared_examples 'create!_with_name' do |text, dup|
describe '.create!' do
context text do
it do
expect { described_class.create! }.to raise_error ActiveRecord::RecordInvalid
end
end
it '1 character name' do
expect { described_class.create!(name: 'a') }.to raise_error ActiveRecord::RecordInvalid
end
it '2+ character name' do
expect { described_class.find_or_create_by!(name: 'as') }.not_to raise_error
end
it 'with duplicate name' do
expect { described_class.create!(name: dup) }.to raise_error ActiveRecord::RecordInvalid
end
end
end
# required attribute shared example
# usage 'required attribute', 'name', 1
RSpec.shared_examples 'required attribute' do |attribute, error_count|
describe "##{attribute}" do
include_context 'restore attributes'
it 'is required' do
subject.send('write_attribute', attribute, nil)
subject.valid?
expect(subject.errors[attribute.to_sym].size).to eq(error_count)
end
end
end
RSpec.shared_examples 'default attribute' do |attribute, default|
describe "##{attribute}" do
include_context 'restore attributes'
it 'is is defaulted' do
subject.send('write_attribute', attribute, default)
expect(subject.send(attribute.to_sym)).to eq(default)
end
end
end
# Tests numeric values
RSpec.shared_examples 'number specs' do |number|
it 'is required' do
subject.send(:write_attribute, number, nil)
subject.valid?
expect(subject.errors[number.to_sym].size).to eq(2)
end
it 'is not negative' do
subject.send(:write_attribute, number, -1)
subject.valid?
expect(subject.errors[number.to_sym].size).to eq(1)
end
context 'is < 10001' do
it do
subject.send(:write_attribute, number, 10_001)
subject.valid?
expect(subject.errors[number.to_sym].size).to eq(1)
end
it 'may equal 10000' do
subject.send(:write_attribute, number, 10_000)
subject.valid?
expect(subject.errors[:number].size).to eq(0)
end
end
context 'is positive' do
it do
subject.send(:write_attribute, number, 0)
subject.valid?
expect(subject.errors[number.to_sym].size).to eq(1)
end
end
context 'is integer' do
it do
subject.send(:write_attribute, number, 1.5)
subject.valid?
expect(subject.errors[number.to_sym].size).to eq(1)
end
end
end
RSpec.shared_examples '#to_s' do |text, comparator|
describe '#to_s' do
context text do
it { expect(subject.to_s).to eq comparator }
end
end
end
# Tests for valid object creation
RSpec.shared_examples 'valid object creation' do |klass|
it "is an instance of #{klass}" do
expect(subject).to be_a klass
end
it 'is valid with valid attributes' do
expect(subject).to be_valid
end
describe '#created_at today' do
# expect record to be created within the last
# 5 minutes to check timestamp works
it 'is created less than 5 minutes ago', timecop: :freeze do
expect(Time.now - subject.created_at).to be < 300
end
end
end
RSpec.shared_examples 'discards' do |item|
before :each do
item.discard
end
context 'does not delete' do
it { expect(item.discarded?).to eq true }
it { expect(item.class.kept.include?(item)).to be_falsey }
end
end