Module: Zizq::Test
- Defined in:
- lib/zizq/test.rb,
lib/zizq/test/client.rb
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) ⇒ Object
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) ⇒ Boolean
Was a job of ‘job_class` enqueued (optionally with matching args)?.
-
.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?`.
-
.enqueued_raw?(queue: nil, type: nil, payload: nil) ⇒ Boolean
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) ⇒ Boolean
Heuristic payload comparison that handles both ‘Zizq::Job`’s serialized format (‘=> […], “kwargs” => {…}`) and ActiveJob’s (‘=> …, “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) ⇒ Object
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-format `type` via `.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) ⇒ Boolean
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) ⇒ Boolean
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 |
# 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 filters[:filter] = ->(job) { job.payload == payload } unless payload.nil? client.enqueued_jobs(**filters).size end |
.payloads_equivalent?(expected, actual) ⇒ Boolean
Heuristic payload comparison that handles both ‘Zizq::Job`’s serialized format (‘=> […], “kwargs” => {…}`) and ActiveJob’s (‘=> …, “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).
182 183 184 185 186 187 188 |
# File 'lib/zizq/test.rb', line 182 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 |