Tweak have_implicit_order_column + tests

* Make the error messaging a little more natural
* Align tests to the current style (specifically using `match_against`)
This commit is contained in:
Elliot Winkler 2020-03-20 12:18:40 -06:00
parent 67af0dfc89
commit 6a2e7551b2
4 changed files with 148 additions and 135 deletions

View File

@ -67,8 +67,8 @@ GEM
bootsnap (1.4.5) bootsnap (1.4.5)
msgpack (~> 1.0) msgpack (~> 1.0)
builder (3.2.4) builder (3.2.4)
byebug (11.1.1) byebug (11.0.1)
capybara (3.31.0) capybara (3.30.0)
addressable addressable
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (~> 1.8) nokogiri (~> 1.8)
@ -79,14 +79,14 @@ GEM
childprocess (3.0.0) childprocess (3.0.0)
coderay (1.1.2) coderay (1.1.2)
concurrent-ruby (1.1.5) concurrent-ruby (1.1.5)
crass (1.0.6) crass (1.0.5)
diff-lcs (1.3) diff-lcs (1.3)
erubi (1.9.0) erubi (1.9.0)
ffi (1.12.1) ffi (1.11.3)
fssm (0.2.10) fssm (0.2.10)
globalid (0.4.2) globalid (0.4.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
i18n (1.8.2) i18n (1.7.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jaro_winkler (1.5.4) jaro_winkler (1.5.4)
jbuilder (2.9.1) jbuilder (2.9.1)
@ -103,31 +103,31 @@ GEM
marcel (0.3.3) marcel (0.3.3)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
method_source (0.9.2) method_source (0.9.2)
mimemagic (0.3.4) mimemagic (0.3.3)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.14.0) minitest (5.13.0)
msgpack (1.3.1) msgpack (1.3.1)
multi_json (1.14.1) multi_json (1.14.1)
nio4r (2.5.2) nio4r (2.5.2)
nokogiri (1.10.7) nokogiri (1.10.7)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
parallel (1.19.1) parallel (1.19.1)
parser (2.7.0.2) parser (2.7.0.0)
ast (~> 2.4.0) ast (~> 2.4.0)
pg (1.2.2) pg (1.2.0)
pry (0.12.2) pry (0.12.2)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
pry-byebug (3.8.0) pry-byebug (3.7.0)
byebug (~> 11.0) byebug (~> 11.0)
pry (~> 0.10) pry (~> 0.10)
public_suffix (4.0.3) public_suffix (4.0.1)
puma (4.3.1) puma (4.3.1)
nio4r (~> 2.0) nio4r (~> 2.0)
pygments.rb (1.2.1) pygments.rb (1.2.1)
multi_json (>= 1.0.0) multi_json (>= 1.0.0)
rack (2.1.2) rack (2.0.8)
rack-proxy (0.6.5) rack-proxy (0.6.5)
rack rack
rack-test (1.1.0) rack-test (1.1.0)
@ -173,12 +173,12 @@ GEM
rspec-core (~> 3.9.0) rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0) rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0) rspec-mocks (~> 3.9.0)
rspec-core (3.9.1) rspec-core (3.9.0)
rspec-support (~> 3.9.1) rspec-support (~> 3.9.0)
rspec-expectations (3.9.0) rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0) rspec-support (~> 3.9.0)
rspec-mocks (3.9.1) rspec-mocks (3.9.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0) rspec-support (~> 3.9.0)
rspec-rails (3.9.0) rspec-rails (3.9.0)
@ -189,20 +189,20 @@ GEM
rspec-expectations (~> 3.9.0) rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0) rspec-mocks (~> 3.9.0)
rspec-support (~> 3.9.0) rspec-support (~> 3.9.0)
rspec-support (3.9.2) rspec-support (3.9.0)
rubocop (0.79.0) rubocop (0.78.0)
jaro_winkler (~> 1.5.1) jaro_winkler (~> 1.5.1)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 2.7.0.1) parser (>= 2.6)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7) unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.4.2) rubocop-rails (2.4.1)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 0.72.0) rubocop (>= 0.72.0)
ruby-progressbar (1.10.1) ruby-progressbar (1.10.1)
ruby_dep (1.5.0) ruby_dep (1.5.0)
rubyzip (2.1.0) rubyzip (2.0.0)
sass-rails (6.0.0) sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1) sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.2.1) sassc (2.2.1)
@ -213,7 +213,7 @@ GEM
sprockets (> 3.0) sprockets (> 3.0)
sprockets-rails sprockets-rails
tilt tilt
selenium-webdriver (3.142.7) selenium-webdriver (3.142.6)
childprocess (>= 0.5, < 4.0) childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2) rubyzip (>= 1.2.2)
shoulda-context (1.2.2) shoulda-context (1.2.2)
@ -239,8 +239,8 @@ GEM
turbolinks-source (5.2.0) turbolinks-source (5.2.0)
tzinfo (1.2.6) tzinfo (1.2.6)
thread_safe (~> 0.1) thread_safe (~> 0.1)
unicode-display_width (1.6.1) unicode-display_width (1.6.0)
webdrivers (4.2.0) webdrivers (4.1.3)
nokogiri (~> 1.6) nokogiri (~> 1.6)
rubyzip (>= 1.3.0) rubyzip (>= 1.3.0)
selenium-webdriver (>= 3.0, < 4.0) selenium-webdriver (>= 3.0, < 4.0)
@ -253,7 +253,7 @@ GEM
websocket-extensions (0.1.4) websocket-extensions (0.1.4)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
yard (0.9.24) yard (0.9.20)
zeitwerk (2.2.2) zeitwerk (2.2.2)
zeus (0.15.14) zeus (0.15.14)
method_source (>= 0.6.7) method_source (>= 0.6.7)
@ -298,4 +298,4 @@ DEPENDENCIES
zeus zeus
BUNDLED WITH BUNDLED WITH
1.17.2 1.17.3

View File

@ -1,8 +1,9 @@
module Shoulda module Shoulda
module Matchers module Matchers
module ActiveRecord module ActiveRecord
# The `have_implicit_order_column` matcher tests that the model has `implicit_order_column` # The `have_implicit_order_column` matcher tests that the model has
# assigned to one of the table columns. (Rails 6+ only) # `implicit_order_column` assigned to one of the table columns.
# (Rails 6+ only.)
# #
# class Product < ApplicationRecord # class Product < ApplicationRecord
# self.implicit_order_column = :created_at # self.implicit_order_column = :created_at
@ -38,52 +39,70 @@ module Shoulda
end end
def failure_message def failure_message
"Expected #{expectation} (#{@details})" message =
if details
"Expected #{expectation} (#{details})."
else
"Expected #{expectation}."
end
Shoulda::Matchers.word_wrap(message)
end end
def failure_message_when_negated def failure_message_when_negated
"Did not expect #{expectation}" Shoulda::Matchers.word_wrap(
"Did not expect #{expectation}, but it did.",
)
end end
def description def description
"have implicit_order_column assigned to #{@column}" "have an implicit_order_column of :#{column}"
end end
private private
def column_exists? attr_reader :column, :subject, :details
matcher = HaveDbColumnMatcher.new(@column)
if matcher.matches?(@subject) def column_exists?
matcher = HaveDbColumnMatcher.new(column)
if matcher.matches?(subject)
true true
else else
@details = "#{model_class} does not have a db column named #{@column}" @details =
"#{model.table_name} does not have #{a_or_an(":#{column}")} " +
'column'
false false
end end
end end
def implicit_order_column_matches? def implicit_order_column_matches?
model_implicit_order_column = model_class.implicit_order_column model_implicit_order_column = model.implicit_order_column
if model_implicit_order_column.to_s == @column.to_s if model_implicit_order_column.to_s == column.to_s
true true
else else
@details = if model_implicit_order_column.nil? @details =
"#{model_class} implicit_order_column is not set" if model_implicit_order_column
else 'its implicit_order_column is ' +
"#{model_class} implicit_order_column is " + ":#{model_implicit_order_column}"
"set to #{model_implicit_order_column}" else
end 'it does not have an implicit_order_column'
end
false false
end end
end end
def model_class def expectation
@subject.class "#{model.name} to have an implicit_order_column of :#{column}"
end end
def expectation def model
"#{model_class.name} to have implicit_order_column set to #{@column}" subject.class
end
def a_or_an(word)
Shoulda::Matchers::Util.a_or_an(word)
end end
end end
end end

View File

@ -33,7 +33,7 @@ module Shoulda
end end
def self.a_or_an(next_word) def self.a_or_an(next_word)
if next_word =~ /\A[aeiou]/i && next_word != 'unique' if next_word =~ /\A:?[aeiou]/i && next_word != 'unique'
"an #{next_word}" "an #{next_word}"
else else
"a #{next_word}" "a #{next_word}"

View File

@ -2,169 +2,163 @@ require 'unit_spec_helper'
describe Shoulda::Matchers::ActiveRecord::HaveImplicitOrderColumnMatcher, type: :model do describe Shoulda::Matchers::ActiveRecord::HaveImplicitOrderColumnMatcher, type: :model do
if active_record_supports_implicit_order_column? if active_record_supports_implicit_order_column?
context 'when implicit_order_column is defined for the column' do context 'when the model sets implicit_order_column to the given column' do
context 'when column name is a symbol' do context 'when the given column name is a symbol' do
it 'accepts' do it 'accepts' do
record = record_with_implicit_order_column_on( record = record_with_implicit_order_column_on(
'created_at', :created_at,
model_name: 'Employee',
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
expect(record).to have_implicit_order_column(:created_at) expect { have_implicit_order_column(:created_at) }.
to match_against(record).
or_fail_with(<<~MESSAGE, wrap: true)
Did not expect Employee to have an implicit_order_column of
:created_at, but it did.
MESSAGE
end end
end end
context 'when column name is a string' do context 'when the given column name is a string' do
it 'accepts' do it 'accepts' do
record = record_with_implicit_order_column_on( record = record_with_implicit_order_column_on(
'created_at', :created_at,
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
expect(record).to have_implicit_order_column('created_at') expect { have_implicit_order_column('created_at') }.
to match_against(record).
or_fail_with(<<~MESSAGE, wrap: true)
Did not expect Employee to have an implicit_order_column of
:created_at, but it did.
MESSAGE
end end
end end
end end
context 'when implicit_order_column is defined for another column' do context 'when the model sets implicit_order_column to another column' do
context 'when column name is a symbol' do context 'when the given column name is a symbol' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_with_implicit_order_column_on( record = record_with_implicit_order_column_on(
'created_at', :created_at,
columns: { created_at: :timestamp, email: :string }, columns: { created_at: :timestamp, email: :string },
) )
assertion = lambda { expect { have_implicit_order_column(:email) }.
expect(record).to have_implicit_order_column(:email) not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :email
message = format_message(<<-MESSAGE, one_line: true) (its implicit_order_column is :created_at).
Expected Employee to have implicit_order_column set to email MESSAGE
(Employee implicit_order_column is set to created_at)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
context 'when column name is a string' do context 'when the given column name is a string' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_with_implicit_order_column_on( record = record_with_implicit_order_column_on(
'created_at', :created_at,
columns: { created_at: :timestamp, email: :string }, columns: { created_at: :timestamp, email: :string },
) )
assertion = lambda { expect { have_implicit_order_column('email') }.
expect(record).to have_implicit_order_column('email') not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :email
message = format_message(<<-MESSAGE, one_line: true) (its implicit_order_column is :created_at).
Expected Employee to have implicit_order_column set to email MESSAGE
(Employee implicit_order_column is set to created_at)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
end end
context 'when implicit_order_column is NOT defined on model' do context 'when the model does NOT set implicit_order_column' do
context 'when column name is a symbol' do context 'when the given column name is a symbol' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_without_implicit_order_column( record = record_without_implicit_order_column(
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
assertion = lambda { expect { have_implicit_order_column(:created_at) }.
expect(record).to have_implicit_order_column(:created_at) not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :created_at
message = format_message(<<-MESSAGE, one_line: true) (it does not have an implicit_order_column).
Expected Employee to have implicit_order_column set to created_at MESSAGE
(Employee implicit_order_column is not set)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
context 'when column name is a string' do context 'when the given column name is a string' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_without_implicit_order_column( record = record_without_implicit_order_column(
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
assertion = lambda { expect { have_implicit_order_column('created_at') }.
expect(record).to have_implicit_order_column('created_at') not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :created_at
message = format_message(<<-MESSAGE, one_line: true) (it does not have an implicit_order_column).
Expected Employee to have implicit_order_column set to created_at MESSAGE
(Employee implicit_order_column is not set)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
end end
context 'when given column does NOT exist' do context 'when the given column does not exist on the table' do
context 'when column name is a symbol' do context 'when the given column name is a symbol' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_without_implicit_order_column( record = record_without_implicit_order_column(
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
assertion = lambda { expect { have_implicit_order_column(:individual) }.
expect(record).to have_implicit_order_column(:whatever) not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :individual
message = format_message(<<-MESSAGE, one_line: true) (employees does not have an :individual column).
Expected Employee to have implicit_order_column set to whatever MESSAGE
(Employee does not have a db column named whatever)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
context 'when column name is a string' do context 'when the given column name is a string' do
it 'rejects with an appropriate failure message' do it 'rejects with an appropriate failure message' do
record = record_without_implicit_order_column( record = record_without_implicit_order_column(
columns: { created_at: :timestamp }, columns: { created_at: :timestamp },
) )
assertion = lambda { expect { have_implicit_order_column('individual') }.
expect(record).to have_implicit_order_column('whatever') not_to match_against(record).
} and_fail_with(<<~MESSAGE, wrap: true)
Expected Employee to have an implicit_order_column of :individual
message = format_message(<<-MESSAGE, one_line: true) (employees does not have an :individual column).
Expected Employee to have implicit_order_column set to whatever MESSAGE
(Employee does not have a db column named whatever)
MESSAGE
expect(&assertion).to fail_with_message(message)
end end
end end
end end
describe 'description' do describe '#description' do
it 'returns correct description' do it 'returns the correct description' do
matcher = have_implicit_order_column(:created_at) matcher = have_implicit_order_column(:created_at)
expect(matcher.description).to \ expect(matcher.description).to eq(
eq('have implicit_order_column assigned to created_at') 'have an implicit_order_column of :created_at',
)
end end
end end
def record_with_implicit_order_column_on(column_name, columns:) def record_with_implicit_order_column_on(
define_model(:employee, columns) do |model| column_name,
model.implicit_order_column = column_name columns:,
end.new model_name: 'Employee'
)
model = define_model(model_name, columns) do |m|
m.implicit_order_column = column_name
end
model.new
end end
def record_without_implicit_order_column(columns:) def record_without_implicit_order_column(columns:, model_name: 'Employee')
define_model(:employee, columns).new define_model(model_name, columns).new
end end
end end
end end