Skip to content

QueueManager.fake().assertPushed(JobClass, ...) has a TypeScript mismatch with constructor-injected jobs #4

@diego-sepulveda-lavin

Description

@diego-sepulveda-lavin

Package version

0.6.0

Describe the bug

Package versions

  • @adonisjs/queue: 0.6.0
  • @boringnode/queue: 0.5.1
  • @adonisjs/core: 7.3.0
  • typescript: 5.9.3

Summary

The queues guide shows constructor-injected jobs using the ProcessPayment example, and the testing section shows class-based assertions with QueueManager.fake().

However, when using a constructor-injected job like the documented ProcessPayment pattern, fakeQueue.assertPushed(ProcessPayment, ...) and fakeQueue.assertNotPushed(ProcessPayment, ...) fail at type-check time.

Using the job name string works as a workaround, but the class-based assertions become unusable for DI jobs.

Expected behavior

A constructor-injected job following the docs should type-check with fake queue assertions:

const fakeQueue = QueueManager.fake()

fakeQueue.assertPushed(ProcessPayment, {
  queue: 'payments',
})

fakeQueue.assertNotPushed(ProcessPayment, {
  queue: 'payments',
})

Actual behavior

TypeScript reports that the job class is not assignable to the matcher type accepted by assertPushed / assertNotPushed.

The error disappears if the custom constructor is removed.

An example error is:

Argument of type 'typeof ProcessPayment' is not assignable to parameter of type 'FakeJobMatcher'

Documentation references

The issue appears when combining these two documented patterns from the queues guide:

Creating jobs / dependency injection

import { inject } from '@adonisjs/core'
import { Job } from '@adonisjs/queue'
import type { JobOptions } from '@adonisjs/queue/types'
import PaymentService from '#services/payment_service'

interface ProcessPaymentPayload {
  orderId: number
  amount: number
  currency: string
}

@inject()
export default class ProcessPayment extends Job<ProcessPaymentPayload> {
  static options: JobOptions = {
    queue: 'payments',
    maxRetries: 3,
  }

  constructor(private paymentService: PaymentService) {
    super()
  }

  async execute() {
    await this.paymentService.charge(
      this.payload.orderId,
      this.payload.amount,
      this.payload.currency
    )
  }
}

Testing with queue fakes

const fakeQueue = QueueManager.fake()

fakeQueue.assertPushed(ProcessPayment, {
  queue: 'payments',
})

Minimal reproduction

import { inject } from '@adonisjs/core'
import { QueueManager, Job } from '@adonisjs/queue'

class PaymentService {
  async charge(orderId: number, amount: number, currency: string) {
    return { orderId, amount, currency }
  }
}

interface ProcessPaymentPayload {
  orderId: number
  amount: number
  currency: string
}

@inject()
class ProcessPayment extends Job<ProcessPaymentPayload> {
  constructor(private paymentService: PaymentService) {
    super()
  }

  async execute() {
    await this.paymentService.charge(
      this.payload.orderId,
      this.payload.amount,
      this.payload.currency
    )
  }
}

const fakeQueue = QueueManager.fake()

fakeQueue.assertPushed(ProcessPayment, {
  queue: 'payments',
})

fakeQueue.assertNotPushed(ProcessPayment, {
  queue: 'payments',
})

Why this seems inconsistent

At runtime, constructor injection works correctly because Adonis queue resolves job instances through the container.

However, the matcher types come from @boringnode/queue, where:

  • FakeJobMatcher accepts string | JobClass | ((job) => boolean)
  • JobClass is typed as new (...args: unknown[]) => T

That constructor type is too strict for DI-style job classes with specific constructor params like:

new (paymentService: PaymentService) => ProcessPayment

So the runtime supports these jobs, but the testing types reject them.

Workaround

Using the job name string instead of the class works:

fakeQueue.assertPushed('ProcessPayment', {
  queue: 'payments',
})

fakeQueue.assertNotPushed('ProcessPayment', {
  queue: 'payments',
})

Notes

This looks like a typings mismatch rather than a runtime bug.

The fake adapter runtime appears to only need the class name / prototype to match jobs, so class-based assertions for injected jobs seem conceptually valid.

It may be that the underlying fix belongs in @boringnode/queue, but I’m opening it here because this is the documented Adonis testing path and the mismatch shows up when following the Adonis docs examples directly.

Reproduction repo

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions