Dry-run a scheduled stock screen and test its failure paths
Before relying on a scheduled stock screen, test when it runs, which steps execute and what it publishes under normal and failed inputs. A final answer saying “updated successfully” is not enough. Replay a fixed set of triggers and provider payloads, compare actual function calls with expected calls, and check logical output identifiers and counts. The deliverable is a repeatable test record, not a subjective rating of a generated explanation.
This guide provides a standalone local test suite for a synthetic stock screen. Its scheduler flag is false, it registers no task and it sends nothing outside the process. The file does not enable Vibe, call a model or establish that CoTrading offers a scheduler. It tests the supplied implementation’s control flow, including intentionally broken test variants that must be detected.
Distinguish workflow design from a rehearsal test
A workflow design states that refresh precedes validation and screening, while publication occurs only after successful checks. A rehearsal must observe those steps actually executing. Here separate methods perform refresh, validation, screening and in-memory publication, and each method appends an event when called. The test compares that trace with a literal expected sequence. The workflow’s own final prose is never the source of evidence.
The fixture uses a fixed UTC+08 timezone, an eligible time of 16:00, and a synthetic calendar with October 20 and 21, 2026 open and October 24 closed. Python datetime documentation supports the aware trigger parsing used here. The test calendar is not an official holiday source. A real integration would require a reviewed calendar and data availability policy in addition to these local tests.
The universe contains AAA and BBB. Each accepted dataset must contain both symbols, the expected date and a finite positive PE. The screen selects PE below 20. AAA at 25 fails; BBB at 18 passes. These simple values keep the test focused on timing, failure paths and repeated execution. They do not validate the financial merits of a strategy or prove access to production market data.
Define observable expectations before running scenarios
For each scenario, specify input trigger, provider payload, expected status, expected call trace and cumulative output count. Use logical slot identifiers such as screen-v1:2026-10-20, rather than random execution IDs. Two triggers for the same successful session should not produce two accepted outputs. A failed attempt should not consume the slot, so a repaired payload can succeed later.
Python unittest provides assertions used by this executable suite. Although the example calls assertions from a small runner, the expected trace and output counts remain independent test data. Do not generate expectations from whatever the implementation just returned. A test that copies the actual trace into its expected value cannot discover a missing refresh or an extra publication.
The selected list alone is an insufficient oracle. A broken implementation could skip refresh but happen to return BBB using old data. It could publish an empty result before reporting that validation failed. It could publish the same correct list twice. Trace order, slot identity and output cardinality distinguish these cases even when a final status or list looks plausible.
Replay six scenarios against one job state
The first trigger is October 20 at 08:00. It returns not_due, has no data calls and leaves output count zero. This establishes that a pre-eligible trigger does not fetch or publish prematurely. The second trigger at 16:30 uses valid October 20 rows, returns success, and executes refresh, validate, screen and publish in order. Output count becomes one, with the October 20 slot and selected BBB.
The third trigger is another October 20 request at 17:00. It returns duplicate with no calls and count still one. The fourth is October 24 at 16:30. It returns closed, also with no calls. These scenarios exercise separate preconditions: a completed logical slot and a non-session date. Neither should be misclassified as a provider failure or a successful empty screen.
The fifth scenario is October 21 at 16:30 with AAA’s PE field removed. Refresh executes, then validation fails. The exact trace contains refresh for October 21 and validate, with no screen or publication. Status is blocked, output count remains one and the October 21 slot is not consumed. Missing data must not become an accepted empty candidate list.
The sixth scenario supplies corrected October 21 data at 16:45. It executes all four methods, returns success and raises total outputs to two. The output slots must be exactly October 20 and October 21, with no duplicate. This recovery within the same state verifies that the earlier failed attempt did not incorrectly mark the day complete.
actual = job.run(trigger, payload)
case.assertEqual(actual, expected_status)
case.assertEqual(job.trace, expected_trace)
case.assertEqual(len(job.outputs), expected_count)
Download the complete example. It requires Python 3.12+, uses the standard library and synthetic data, and runs with python R556.py. The printed record includes every scenario’s actual trace and output count. The file is independent of other tutorial downloads and needs no scheduler, network, credentials or model installation.
Inject broken implementations to test the tests
First deliberately bypass duplicate suppression. The second October 20 trigger now attempts another execution and publication. The suite detects the unexpected status or calls instead of accepting the repeated correct shortlist. This is a test-only mutation, not a supported production mode. Keeping the expected output count fixed also exposes repeated external effects that a candidate-list comparison would miss.
Next deliberately skip the refresh method while passing the fixture data directly onward. The final list may still be BBB, but the required refresh event is absent. The literal trace assertion fails. This tests whether the suite checks the promised dependency chain rather than rewarding a coincidentally correct final answer. With live data, a similar defect could reuse an old snapshot without any visible numerical difference on a quiet day.
Finally inject publication inside the validation-failure branch. The method still returns blocked, but has already appended a result and consumed the slot. The missing-field scenario catches the extra publication event and changed output count. A final-status-only test would miss this defect. It is particularly important because a seemingly safe error message does not undo an effect that happened before validation completed.
Repair and replay with fixed inputs
The executable runner asserts that all three mutations are detected, then runs the unmodified implementation again. The six-scenario evidence must match the original successful baseline. Preserve baseline, mutation failures and repaired replay together. A passing final test alone cannot show that the test suite would have noticed the defects it was intended to guard against.
Numeric validation adds another boundary. Python Decimal supports special values, so the script separately injects NaN and positive and negative infinity into the PE field. Each attempt must be blocked before producing an output. These cases test invalid data content, while the six timing scenarios test scheduling and state transitions; neither group substitutes for the other.
Decide what this evidence permits you to claim
The result demonstrates the local job’s behavior for the stated calendar, timestamps, payloads and mutation set. It does not test a real scheduling engine, exchange calendar feed, data vendor, persistence layer or notification destination. Do not describe it as a running automation. If integrating those components later, add tests at their actual boundaries and retain the same observable slot, trace and output requirements.
For a reviewed deployment plan, include timezone configuration, rule version, input contract, retry policy, durable duplicate handling and publication destination. Keep scheduling disabled until those components are implemented and separately authorized. The current tutorial stops at a concrete executable rehearsal: six scenarios pass, three deliberate defects are caught and the repaired implementation reproduces the baseline.
FAQ
Is checking the final shortlist enough?
No. The same list can result from skipped refresh, stale inputs or duplicate publication. Check calls, dates and counts too.
Does this file start a background scheduler?
No. It runs a local rehearsal and test mutations once, with scheduling disabled.