Skip to content

Commit

Permalink
Fake queries should look and feel like real ones in test
Browse files Browse the repository at this point in the history
  • Loading branch information
stevegeek committed Oct 7, 2024
1 parent 926fae7 commit 00b2f99
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 19 deletions.
29 changes: 29 additions & 0 deletions lib/quo/fakes/collection_backed_fake.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

# rbs_inline: enabled

module Quo
module Fakes
class CollectionBackedFake < CollectionBackedQuery
prop :results, _Any, reader: false
prop :page_count, _Nilable(Integer), reader: false

def collection
@results
end

def results
klass = Class.new(CollectionResults) do
def page_count
@query.page_count
end
end
klass.new(self)
end

def page_count
@page_count || validated_query.size
end
end
end
end
52 changes: 52 additions & 0 deletions lib/quo/fakes/relation_backed_fake.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

# rbs_inline: enabled

module Quo
module Fakes
class RelationBackedFake < RelationBackedQuery
prop :results, _Any, reader: false
prop :page_count, _Nilable(Integer), reader: false
prop :total_count, _Nilable(Integer), reader: false

def query
@results
end

def results
klass = Class.new(RelationResults) do
def page_count
@query.page_count
end

def total_count
@query.total_count
end
end
klass.new(self)
end

def page_count
@page_count || validated_query.size
end

def total_count
@total_count || validated_query.size
end

private

def validated_query
query
end

def underlying_query
validated_query
end

def configured_query
validated_query
end
end
end
end
46 changes: 27 additions & 19 deletions lib/quo/minitest/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,40 @@

require "minitest/mock"

require_relative "../fakes/collection_backed_fake"
require_relative "../fakes/relation_backed_fake"

module Quo
module Minitest
module Helpers
# Stub query takes a block, and will yield to it with the stub on `new` set up.]
# Optionally, you can provide a mock to use for the fake query, instead of using a CollectionBackedQuery instance.
def stub_query(query_class, mock: nil, results: [])
raise "stub_query requires a block" unless block_given?
def fake_query(query_class, results: [], total_count: nil, page_count: nil)
# make it so that results of instances of this class return a fake Result object
# of the right type which returns the results passed in
if query_class < Quo::CollectionBackedQuery
klass = Class.new(Quo::Fakes::CollectionBackedFake) do
if query_class < Quo::Preloadable
include Quo::Preloadable

fake = if mock
proc { |*p, **k, &b| mock.new(*p, **k, &b) }
def query
collection
end
end
end
query_class.stub(:new, ->(**kwargs) {
klass.new(**kwargs, results: results, total_count: total_count, page_count: page_count)
}) do
yield
end
elsif query_class < Quo::RelationBackedQuery
query_class.stub(:new, ->(**kwargs) {
Quo::Fakes::RelationBackedFake.new(**kwargs, results: results, total_count: total_count, page_count: page_count)
}) do
yield
end
else
::Quo::CollectionBackedQuery.wrap(results).new
end
query_class.stub(:new, fake) do
yield
raise ArgumentError, "Not a Query class: #{query_class}"
end
end

# Return a Mock query. This can then be used with `stub_query` if desired.
# `args`/`kwargs` are optional, and if provided, the mock will expect `new` with the given arguments.
def mock_query(query_class, args: [], kwargs: nil, results: [])
query_class_mock = ::Minitest::Mock.new(query_class)
fake_qo_instance = ::Quo::CollectionBackedQuery.wrap(results).new
query_class_mock.expect(:new, fake_qo_instance, args, **kwargs)
query_class_mock
end
end
end
end
45 changes: 45 additions & 0 deletions test/quo/fake_query_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require_relative "../test_helper"
require "quo/minitest/helpers"

class Quo::FakeQueryTest < ActiveSupport::TestCase
include Quo::Minitest::Helpers

def create_query_class
Class.new(Quo::CollectionBackedQuery) do
include Quo::Preloadable

def collection
[1, 2, 3]
end
end
end

test "RelationBackedFake acts like a RelationBackedQuery" do
fake_query(UnreadCommentsQuery, results: [1, 2]) do
q = UnreadCommentsQuery.new
assert q.results.is_a?(Quo::RelationResults)
assert q.is_a?(Quo::RelationBackedQuery)
assert_equal 2, q.results.count
assert_equal 1, q.results.first
assert_nothing_raised do
q.includes(:foo).order(:bar).limit(10).preload(:x).results.first
end
end
end

test "CollectionBackedFake acts like a CollectionBackedQuery" do
klass = create_query_class
fake_query(klass, results: [1, 2]) do
q = klass.new
assert q.results.is_a?(Quo::CollectionResults)
assert q.is_a?(Quo::CollectionBackedQuery)
assert_equal 2, q.results.count
assert_equal 1, q.results.first
assert_nothing_raised do
q.preload(:x).results.first
end
end
end
end

0 comments on commit 00b2f99

Please sign in to comment.