Testing Background Jobs¶
Deepstaging provides TestJobScheduler and TestJobStore — test doubles with call recording and typed query APIs. No mocking libraries needed.
TestJobScheduler¶
Records every Enqueue, Schedule, Cancel, and Retry call. Query recorded calls by payload type.
var scheduler = new TestJobScheduler();
// ... run your handler that enqueues jobs ...
// Assert a specific job type was enqueued
var emails = scheduler.Enqueued<SendWelcomeEmail>();
await Assert.That(emails).Count().IsEqualTo(1);
await Assert.That(emails[0].Email).IsEqualTo("alice@example.com");
Typed Queries¶
| Method | Returns |
|---|---|
Enqueued<T>() |
IReadOnlyList<T> — all enqueued payloads of type T |
Scheduled<T>() |
IReadOnlyList<(T Job, DateTimeOffset RunAt)> — scheduled payloads with run times |
Raw Recording Lists¶
| Property | Type | Description |
|---|---|---|
EnqueueCalls |
IReadOnlyList<EnqueuedJob> |
All enqueue calls (untyped) |
ScheduleCalls |
IReadOnlyList<ScheduledJob> |
All schedule calls (untyped) |
CancelCalls |
IReadOnlyList<JobId> |
All cancel calls |
RetryCalls |
IReadOnlyList<JobId> |
All retry calls |
Configurable Responses¶
var scheduler = new TestJobScheduler();
// Configure GetStatusAsync to return a custom result
scheduler.OnGetStatus = id => new JobInfo(
id, "SendWelcomeEmail", JobStatus.Completed, "{}",
null, 1, 3, 1000, 300000, 0, null,
DateTimeOffset.UtcNow, null, DateTimeOffset.UtcNow, null);
// Configure CancelAsync to return false
scheduler.OnCancel = _ => false;
TestJobStore¶
Records Complete, Fail, ClaimNext, and Heartbeat calls. Seed jobs for the worker to process.
var store = new TestJobStore();
// Seed a job for the worker to claim
store.Seed(new JobInfo(
JobId.New(), "SendWelcomeEmail", JobStatus.Pending, payload,
null, 0, 3, 1000, 300000, 0, null,
DateTimeOffset.UtcNow, null, null, null));
// ... run the worker ...
// Assert job lifecycle
await Assert.That(store.CompletedJobIds).Count().IsEqualTo(1);
Assertion Properties¶
| Property | Type | Description |
|---|---|---|
CompletedJobIds |
IReadOnlyList<JobId> |
Jobs marked completed |
FailedJobs |
IReadOnlyList<(JobId, string?)> |
Jobs marked failed, with error messages |
ClaimedJobIds |
IReadOnlyList<JobId> |
Jobs claimed by the worker |
HeartbeatCalls |
IReadOnlyList<JobId> |
Jobs that received heartbeats |
Jobs |
IReadOnlyDictionary<JobId, JobInfo> |
All jobs in the store |
Wiring Into TestRuntime¶
Wire the test doubles via OnConfigure():
[TestRuntime<AppRuntime>]
public partial class TestAppRuntime
{
partial void OnConfigure() =>
WithJobScheduler(new TestJobScheduler())
.WithJobStore(new TestJobStore());
}
Then assert in your test:
[Test]
public async Task CreateOrder_EnqueuesWelcomeEmail()
{
var runtime = TestAppRuntime.CreateConfigured();
var program = AppDispatch.Dispatch(new CreateOrder("Alice"));
await program.RunAsync(runtime);
var scheduler = (TestJobScheduler)runtime.JobScheduler;
await Assert.That(scheduler.Enqueued<SendWelcomeEmail>())
.Single();
}
Reset Between Tests¶
Both test doubles support Reset() to clear recorded calls: