Skip to content

Instantly share code, notes, and snippets.

@AlexB52

AlexB52/README.md

Last active Feb 8, 2021
Embed
What would you like to do?
Testing ActiveRecord Concerns

Testing ActiveRecord Concerns

This gist illustrates the full spec suite of the example used in Testing ActiveRecord Concerns

Copy the testing-concerns.rb file and run rspec testing-concerns.rb

The output should look something like

... bundling ...
... some database logs ...

Finished in 0.04126 seconds (files took 6.61 seconds to load)
10 examples, 0 failures
# frozen_string_literal: true
# -- RAILS SETUP (ignore) --
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "rails", github: "rails/rails", branch: "main"
gem 'rspec-rails', '~> 4.0.2'
gem "sqlite3"
gem 'byebug'
end
require 'byebug'
require "active_record"
require "minitest/autorun"
require "logger"
require 'rails/all'
# This connection will do for database-independent bug reports.
ActiveRecord::Base.configurations = { "test" => { adapter: "sqlite3", database: "database.sqlite" } }
ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations['test']
ActiveRecord::Base.logger = Logger.new(STDOUT)
# -- RAILS APP --
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.datetime :reviewed_at
t.string :title, null: false
end
end
# requires #reviewed_at:datetime column
module Reviewable
extend ActiveSupport::Concern
included do
scope :reviewed, -> { where.not(reviewed_at: nil) }
scope :unreviewed, -> { where(reviewed_at: nil) }
end
def reviewed?
reviewed_at.present?
end
def review(time = DateTime.current)
update(reviewed_at: time)
end
end
class Post < ActiveRecord::Base
include Reviewable
validates_presence_of :title
end
# -- RAILS SPEC HELPER --
module InMemoryDatabaseHelpers
extend ActiveSupport::Concern
class_methods do
def switch_to_SQLite(&block)
before(:all) { switch_to_in_memory_database(&block) }
after(:all) { switch_back_to_test_database }
end
end
private
def switch_to_in_memory_database(&block)
raise 'No migration given' unless block_given?
ActiveRecord::Migration.verbose = false
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.define(version: 1, &block)
end
def switch_back_to_test_database
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
end
end
# -- RAILS SPEC SUITE --
# requires a :reviewable object
shared_examples 'reviewable'do
describe 'the interface' do
subject { described_class.new }
it 'has the correct interface' do
expect(subject).to respond_to(:reviewed?)
expect(subject).to respond_to(:review)
expect(described_class).to respond_to(:reviewed)
expect(described_class).to respond_to(:unreviewed)
end
end
describe 'scopes' do
let!(:records) { described_class.where(id: [reviewed, unreviewed]) }
before do
expect(reviewed.reviewed?).to eql true
expect(unreviewed.reviewed?).to eql false
end
describe '.reviewed' do
subject { records.reviewed }
it { is_expected.to eq [reviewed] }
end
describe '.unreviewed' do
subject { records.unreviewed }
it { is_expected.to eq [unreviewed] }
end
end
describe '#reviewed?' do
subject { described_class.new }
it 'returns the correct boolean based on #reviewed_at' do
subject.reviewed_at = nil
expect(subject.reviewed?).to eql false
subject.reviewed_at = DateTime.current
expect(subject.reviewed?).to eql true
end
end
describe '#review' do
let(:time) { DateTime.current }
subject { unreviewed.review(time) }
it 'updates the reviewed_at attribute' do
expect { subject }.to change { unreviewed.reload.reviewed_at }.from(nil).to(time)
end
end
end
class FakeReviewable < ActiveRecord::Base
include Reviewable
end
describe Reviewable do
include InMemoryDatabaseHelpers
switch_to_SQLite do
create_table :fake_reviewables do |t|
t.datetime :reviewed_at
end
end
describe FakeReviewable, type: :model do
include_examples 'reviewable' do
let(:reviewed) { FakeReviewable.create! reviewed_at: DateTime.current }
let(:unreviewed) { FakeReviewable.create! reviewed_at: nil }
end
end
end
# This example is at the end to show that the Rails app went back to the current testing database configuration defined in the setup
describe Post do
include_examples 'reviewable' do
let(:reviewed) { Post.create! title: 'title1', reviewed_at: DateTime.current }
let(:unreviewed) { Post.create! title: 'title2', reviewed_at: nil }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment