Module: Zizq::Test
- Defined in:
- lib/zizq/test.rb,
lib/zizq/test/client.rb,
sig/generated/zizq/test.rbs,
sig/generated/zizq/test/client.rbs
Overview
Test-mode helpers. Activated by setting c.test_mode = true in a
Zizq.configure block; Zizq.client then lazily resolves to a
Zizq::Test::Client that buffers enqueues instead of dispatching.
Typical use in a test helper:
Zizq.configure do |c|
c.test_mode = true
end
class MyTestCase
setup { Zizq::Test.reset! }
end
In a test:
def test_signup_fans_out
# Default buffered mode — assert what was enqueued.
SignupService.new.run
assert_equal 2, Zizq::Test.client.pending_jobs.size
# Drain whatever's pending (handler re-enqueues fall through
# naturally — drain loops until pending is empty).
Zizq::Test.dispatch_enqueued_jobs
# Block form: run the work, then drain. Matches ActiveJob's
# `perform_enqueued_jobs do ... end`.
Zizq::Test.dispatch_enqueued_jobs { SignupService.new.run }
# Filter by queue and/or type when only a subset should fire.
Zizq::Test.dispatch_enqueued_jobs(queue: "emails")
Zizq::Test.dispatch_enqueued_jobs(type: SendEmailJob)
end
Defined Under Namespace
Classes: Client
Class Method Summary collapse
-
.client ⇒ Object
The active test client.
-
.disable! ⇒ Object
Switch back to the real client.
-
.dispatch_enqueued_jobs(**filters, &block) ⇒ void
Dispatch pending jobs through the configured dequeue middleware chain (
Zizq.configuration.dequeue_middleware— same path the real worker uses, so any registered middlewares run in tests too), looping until no more pending entries match the filters. -
.enable! ⇒ Object
Switch Zizq into test mode.
-
.enqueued?(job_class, *args, **kwargs) ⇒ Object
Was a job of
job_classenqueued (optionally with matching args)?. -
.enqueued_count(job_class, *args, **kwargs) ⇒ Object
How many times was a job of
job_classenqueued (optionally with matching args)? Same argument semantics as#enqueued?. -
.enqueued_raw?(queue: nil, type: nil, payload: nil) ⇒ Object
Was a raw job (queue + type + payload) enqueued? Each kwarg is optional; unspecified means "don't filter on this axis.".
-
.enqueued_raw_count(queue: nil, type: nil, payload: nil) ⇒ Object
How many raw jobs match (queue + type + payload)? Same argument semantics as
#enqueued_raw?. -
.payloads_equivalent?(expected, actual) ⇒ Object
Heuristic payload comparison that handles both
Zizq::Job's serialized format ({"args" => [...], "kwargs" => {...}}) and ActiveJob's ({"job_class" => ..., "arguments" => [...], "job_id" => ..., "enqueued_at" => ..., ...}). -
.reset! ⇒ Object
Reset buffered state between tests.
Class Method Details
.client ⇒ Object
The active test client. Raises if test mode is not enabled — better to fail loudly than return a stale or wrong client.
60 61 62 63 64 65 66 |
# File 'lib/zizq/test.rb', line 60 def self.client #: () -> Client unless Zizq.configuration.test_mode raise Client::NotSupported, "Zizq.configuration.test_mode is not enabled; Zizq::Test.client has nothing to manage." end Zizq.client #: Client end |
.disable! ⇒ Object
Switch back to the real client. The buffered state is dropped
along with the test client (the next Zizq.client access
builds a fresh Zizq::Client).
54 55 56 |
# File 'lib/zizq/test.rb', line 54 def self.disable! #: () -> void Zizq.configure { |c| c.test_mode = false } end |
.dispatch_enqueued_jobs(**filters, &block) ⇒ void
This method returns an undefined value.
Dispatch pending jobs through the configured dequeue middleware
chain (Zizq.configuration.dequeue_middleware — same path the
real worker uses, so any registered middlewares run in tests
too), looping until no more pending entries match the filters.
- No block: drain whatever's pending now.
- With block: yield first (test code enqueues), then drain.
only_queues:/except_queues:— String or Array of Strings.only_types:/except_types:— String, Class, or Array of those (Class names match the serialized-formattypevia.to_s, so passing an ActiveJob class works directly).filter:— a lambda->(job)returning truthy to keep an entry. Defaults to "pass all". Combines with the named filters via AND.
Returns the number of jobs dispatched. A block exception
propagates without draining; a handler exception during drain
transitions that entry to dead and re-raises.
92 93 94 95 |
# File 'lib/zizq/test.rb', line 92 def self.dispatch_enqueued_jobs(**filters, &block) #: (**untyped) ?{ () -> void } -> Integer yield if block client.drain(**filters) end |
.enable! ⇒ Object
Switch Zizq into test mode. After this, Zizq.client resolves
to a Zizq::Test::Client that buffers enqueues in memory.
Typically called once in a test helper.
47 48 49 |
# File 'lib/zizq/test.rb', line 47 def self.enable! #: () -> void Zizq.configure { |c| c.test_mode = true } end |
.enqueued?(job_class, *args, **kwargs) ⇒ Object
Was a job of job_class enqueued (optionally with matching
args)?
Zizq::Test.enqueued?(SendEmailJob) # any args
Zizq::Test.enqueued?(SendEmailJob, 42, template: "welcome") # exact args
With no args, matches by class/type only. With args/kwargs,
uses the class's own zizq_serialize to compute the expected
payload — so it works for both Zizq::Job and AJ classes
(AJ's volatile fields like job_id are ignored, only the
arguments subset is compared).
For anything fuzzier (matchers, subset matching, custom
predicates), drop down to
client.enqueued_jobs(only_types: ..., filter: ->(job) { ... }).
132 133 134 |
# File 'lib/zizq/test.rb', line 132 def self.enqueued?(job_class, *args, **kwargs) #: (Class, *untyped, **untyped) -> bool enqueued_count(job_class, *args, **kwargs) > 0 end |
.enqueued_count(job_class, *args, **kwargs) ⇒ Object
How many times was a job of job_class enqueued (optionally
with matching args)? Same argument semantics as #enqueued?.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/zizq/test.rb', line 138 def self.enqueued_count(job_class, *args, **kwargs) #: (Class, *untyped, **untyped) -> Integer type = job_class.to_s return enqueued_raw_count(type: type) if args.empty? && kwargs.empty? unless job_class.respond_to?(:zizq_serialize) raise ArgumentError, "#{job_class} doesn't implement zizq_serialize — " \ "include Zizq::Job or extend Zizq::ActiveJobConfig, " \ "or use Zizq::Test.enqueued_raw? for raw enqueues." end expected = job_class.zizq_serialize(*args, **kwargs) client.enqueued_jobs(only_types: type).count do |job| payloads_equivalent?(expected, job.payload) end end |
.enqueued_raw?(queue: nil, type: nil, payload: nil) ⇒ Object
Was a raw job (queue + type + payload) enqueued? Each kwarg is optional; unspecified means "don't filter on this axis."
Zizq::Test.enqueued_raw?(type: "send_email")
Zizq::Test.enqueued_raw?(type: "send_email", payload: { user_id: 42 })
Zizq::Test.enqueued_raw?(queue: "emails", type: "send_email")
161 162 163 |
# File 'lib/zizq/test.rb', line 161 def self.enqueued_raw?(queue: nil, type: nil, payload: nil) #: (?queue: String?, ?type: String?, ?payload: untyped) -> bool enqueued_raw_count(queue: queue, type: type, payload: payload) > 0 end |
.enqueued_raw_count(queue: nil, type: nil, payload: nil) ⇒ Object
How many raw jobs match (queue + type + payload)? Same
argument semantics as #enqueued_raw?.
167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/zizq/test.rb', line 167 def self.enqueued_raw_count(queue: nil, type: nil, payload: nil) #: (?queue: String?, ?type: String?, ?payload: untyped) -> Integer filters = {} filters[:only_queues] = queue if queue filters[:only_types] = type if type unless payload.nil? # Normalize the assertion-side payload the same way enqueue # normalizes the buffered one, so symbol-keyed test payloads # still match the (string-keyed) wire-format buffer. normalized = Client.normalize_payload(payload) filters[:filter] = ->(job) { job.payload == normalized } end client.enqueued_jobs(**filters).size end |
.payloads_equivalent?(expected, actual) ⇒ Object
Heuristic payload comparison that handles both Zizq::Job's
serialized format ({"args" => [...], "kwargs" => {...}}) and
ActiveJob's ({"job_class" => ..., "arguments" => [...], "job_id" => ..., "enqueued_at" => ..., ...}). The AJ shape
always has an "arguments" key while Zizq::Job's doesn't, so
we use that to pick whether to compare the full hash or only the
arguments subset (dropping AJ's volatile per-enqueue fields).
188 189 190 191 192 193 194 |
# File 'lib/zizq/test.rb', line 188 def self.payloads_equivalent?(expected, actual) #: (untyped, untyped) -> bool if expected.is_a?(Hash) && expected.key?("arguments") actual.is_a?(Hash) && actual["arguments"] == expected["arguments"] else actual == expected end end |
.reset! ⇒ Object
Reset buffered state between tests. Keeps the configured
test_mode flag.
70 71 72 |
# File 'lib/zizq/test.rb', line 70 def self.reset! #: () -> void client.clear! end |