test: cover OrmEagerLoadingTest and teardown
This commit is contained in:
1 parent
afe1cd4e81
commit
11e15aec01
1 file changed
+127
@@ -0,0 +1,127 @@
|
||||
require 'minitest/autorun'
|
||||
require 'prism'
|
||||
require 'tmpdir'
|
||||
|
||||
class OrmEagerLoadingTest < Minitest::Test
|
||||
def setup
|
||||
@dir = Dir.mktmpdir('prism-eager-store')
|
||||
@store = CountingStore.new(File.join(@dir, 'test.pstore'))
|
||||
@store.create_table(:authors, %w[id name])
|
||||
@store.create_table(:posts, %w[id title author_id])
|
||||
@store.create_table(:bios, %w[id author_id headline])
|
||||
|
||||
author_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :authors
|
||||
has_many :posts, class_name: 'Post', foreign_key: 'author_id'
|
||||
has_one :bio, class_name: 'Bio', foreign_key: 'author_id'
|
||||
end
|
||||
post_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :posts
|
||||
belongs_to :author, class_name: 'Author', foreign_key: 'author_id'
|
||||
end
|
||||
bio_class = Class.new(Prism::Orm::Model) do
|
||||
table_name :bios
|
||||
belongs_to :author, class_name: 'Author', foreign_key: 'author_id'
|
||||
end
|
||||
[author_class, post_class, bio_class].each { |klass| klass.store = @store }
|
||||
Object.const_set(:Author, author_class)
|
||||
Object.const_set(:Post, post_class)
|
||||
Object.const_set(:Bio, bio_class)
|
||||
end
|
||||
|
||||
def teardown
|
||||
FileUtils.remove_entry(@dir)
|
||||
%i[Author Post Bio].each { |name| Object.send(:remove_const, name) if Object.const_defined?(name) }
|
||||
end
|
||||
|
||||
def test_includes_has_many_avoids_n_plus_one_queries
|
||||
3.times do |i|
|
||||
author = Author.create!(name: "author#{i}")
|
||||
2.times { |j| Post.create!(title: "post#{i}-#{j}", author_id: author.id) }
|
||||
end
|
||||
|
||||
@store.reset_query_count!
|
||||
authors = Author.all.includes(:posts).to_a
|
||||
baseline_queries = @store.query_count
|
||||
|
||||
authors.each { |author| author.posts.each(&:title) }
|
||||
assert_equal baseline_queries, @store.query_count,
|
||||
"expected preloaded #posts to be free, but it issued #{@store.query_count - baseline_queries} more queries"
|
||||
|
||||
total_posts = authors.sum { |author| author.posts.size }
|
||||
assert_equal 6, total_posts
|
||||
end
|
||||
|
||||
def test_includes_belongs_to_preloads_single_target
|
||||
author = Author.create!(name: 'solo')
|
||||
Post.create!(title: 'a', author_id: author.id)
|
||||
Post.create!(title: 'b', author_id: author.id)
|
||||
|
||||
@store.reset_query_count!
|
||||
posts = Post.all.includes(:author).to_a
|
||||
baseline = @store.query_count
|
||||
names = posts.map { |post| post.author.name }
|
||||
|
||||
assert_equal baseline, @store.query_count
|
||||
assert_equal %w[solo solo], names
|
||||
end
|
||||
|
||||
def test_includes_has_one_preloads
|
||||
author = Author.create!(name: 'writer')
|
||||
Bio.create!(headline: 'hello there', author_id: author.id)
|
||||
|
||||
loaded = Author.all.includes(:bio).to_a.first
|
||||
assert_equal 'hello there', loaded.bio.headline
|
||||
end
|
||||
|
||||
def test_includes_unknown_association_raises
|
||||
Author.create!(name: 'x')
|
||||
assert_raises(ArgumentError) { Author.all.includes(:nope).to_a }
|
||||
end
|
||||
|
||||
def test_includes_reduces_queries_compared_to_lazy_access
|
||||
3.times do |i|
|
||||
author = Author.create!(name: "author#{i}")
|
||||
2.times { |j| Post.create!(title: "post#{i}-#{j}", author_id: author.id) }
|
||||
end
|
||||
|
||||
@store.reset_query_count!
|
||||
Author.all.to_a.each { |author| author.posts.to_a }
|
||||
lazy_queries = @store.query_count
|
||||
|
||||
@store.reset_query_count!
|
||||
Author.all.includes(:posts).to_a.each { |author| author.posts.to_a }
|
||||
eager_queries = @store.query_count
|
||||
|
||||
assert_equal 4, lazy_queries # 1 authors query + 1 posts query per author (N+1)
|
||||
assert_equal 2, eager_queries # 1 authors query + 1 batched posts query
|
||||
assert_operator eager_queries, :<, lazy_queries
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps Store#all/#find to count reads, so tests can assert on actual
|
||||
# query volume instead of trusting behavior "looks" batched.
|
||||
class CountingStore < Prism::Orm::Store
|
||||
def initialize(*)
|
||||
super
|
||||
@query_count = 0
|
||||
end
|
||||
|
||||
def query_count
|
||||
@query_count
|
||||
end
|
||||
|
||||
def reset_query_count!
|
||||
@query_count = 0
|
||||
end
|
||||
|
||||
def all(table)
|
||||
@query_count += 1
|
||||
super
|
||||
end
|
||||
|
||||
def find(table, id)
|
||||
@query_count += 1
|
||||
super
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user