Skip to content

Commit b7f062f

Browse files
authored
Implement a new matcher, only_load_at_most_models (#28)
* Add a .ruby-version file * Start implementation of a only_load_at_most_models expectation * Test and implement LTE behavior * Add gitignore record for .byebug_history * Add a test to catch the case where we do not load any users at all * Improve our error message a little bit, write a failing test * Update VERSION and CHANGELOG
1 parent 9be565b commit b7f062f

File tree

7 files changed

+97
-2
lines changed

7 files changed

+97
-2
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
# rspec failure tracking
1212
.rspec_status
1313
Gemfile.lock
14-
/gemfiles
14+
/gemfiles
15+
.byebug_history

.ruby-version

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2.7.5
2+

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.7.0] - 2022-01-27
10+
### Added
11+
- A new matcher, `only_load_at_most_models`, will do a less-than-or-equal-to (<=) check on model counts. This is a method that makes tests less noisy as performance gets better.
12+
913
## [0.6.0] - 2022-01-05
1014
### Changed
1115
- Support Rails 7

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.6.0
1+
0.7.0

lib/ar_query_matchers.rb

+43
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,49 @@ def failure_text
9797
end
9898
end
9999

100+
# The following will fail because the call to `User` is not expected, even
101+
# though the Payroll count is correct:
102+
#
103+
# expect {
104+
# Payroll.count
105+
# Payroll.count
106+
# User.count
107+
# }.to only_load_at_most_models(
108+
# 'Payroll' => 2,
109+
# )
110+
#
111+
# The following will succeed because the counts are exact:
112+
#
113+
# expect {
114+
# Payroll.count
115+
# Payroll.count
116+
# User.count
117+
# }.to only_load_at_most_models(
118+
# 'Payroll' => 2,
119+
# 'User' => 1,
120+
# )
121+
#
122+
RSpec::Matchers.define(:only_load_at_most_models) do |expected = {}|
123+
include MatcherConfiguration
124+
include MatcherErrors
125+
126+
match do |block|
127+
@query_stats = Queries::LoadCounter.instrument(&block)
128+
expected_queries = Utility.remove_superfluous_expectations(expected)
129+
actual_queries = @query_stats.query_counts
130+
131+
all_models = expected_queries.keys | actual_queries.keys
132+
133+
all_models.each do |model|
134+
expect(actual_queries[model] || 0).to be <= expected_queries[model]
135+
end
136+
end
137+
138+
def failure_text
139+
expectation_failed_message('load at most')
140+
end
141+
end
142+
100143
RSpec::Matchers.define(:not_load_any_models) do
101144
include MatcherConfiguration
102145
include MatcherErrors

query-matchers.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
3232

3333
spec.add_development_dependency 'appraisal'
3434
spec.add_development_dependency 'bundler'
35+
spec.add_development_dependency 'byebug'
3536
spec.add_development_dependency 'rake'
3637
spec.add_development_dependency 'rspec'
3738
spec.add_development_dependency 'rubocop'

spec/ar_query_matchers/query_matchers_spec.rb

+44
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,48 @@ def updates(amount)
167167
)
168168
end
169169
end
170+
171+
describe 'only_load_at_most_models' do
172+
it 'succeeds' do
173+
expect do
174+
updates(1)
175+
loads(5)
176+
creates(1)
177+
end.to only_load_at_most_models({ 'MockUser' => 5 })
178+
end
179+
180+
context 'there are fewer requests for models' do
181+
it 'succeeds' do
182+
expect do
183+
updates(1)
184+
loads(4)
185+
creates(1)
186+
end.to only_load_at_most_models({ 'MockUser' => 5 })
187+
end
188+
end
189+
190+
context 'there is no request for something specified' do
191+
it 'succeeds' do
192+
expect do
193+
updates(1)
194+
creates(1)
195+
end.to only_load_at_most_models({ 'MockUser' => 5 })
196+
end
197+
end
198+
199+
context 'there are more requests made' do
200+
it 'fails' do
201+
expect do
202+
expect do
203+
updates(1)
204+
loads(6)
205+
creates(1)
206+
end.to only_load_at_most_models({ 'MockUser' => 5 })
207+
end.to raise_error(
208+
RSpec::Expectations::ExpectationNotMetError,
209+
/Expected ActiveRecord to load at most {"MockUser"=>5}, got {"MockUser"=>6}.*Where unexpected queries came from:/m
210+
)
211+
end
212+
end
213+
end
170214
end

0 commit comments

Comments
 (0)