ACF Test Strategy, Fixtures, and Scenario Matrix
ACF testing works when fixture data matches real editorial behavior, not just ideal input values.
You will build deterministic fixture packs, map lifecycle scenarios across ACF hooks, and validate coverage with CLI commands that can run in local and CI environments.
Concept Overview
ACF bugs are frequently workflow bugs instead of syntax bugs. Fields look valid in isolation, but failures appear when drafts become publish candidates, when legacy meta formats coexist with new schemas, or when editors submit partial data. A good test strategy starts by reproducing those states deliberately.
Fixtures are your controlled content states. For ACF projects, a useful fixture set always includes at least one golden record, one partial record, and one legacy-format record. Golden records confirm expected behavior. Partial records confirm fallback and guard behavior. Legacy records confirm compatibility logic after schema changes.
A scenario matrix turns these fixtures into explicit coverage commitments. Each matrix row should include the input state, the hook stage, the expected validation behavior, and the expected post-save or render outcome. When each row maps to one automated test ID, you can detect coverage gaps before release. That traceability is what converts a test document into an engineering control.
The matrix also helps teams coordinate CI priorities.
Not every scenario needs a full browser test.
Many ACF behaviors can be validated with wp eval, wp eval-file, and small PHPUnit cases that apply hooks directly.
This keeps test feedback fast while still catching high-impact data workflow issues.
Treat fixture design and scenario mapping as first-class code artifacts, then verify them with repeatable CLI checks before every release.
Mental Model
| Think of it as | Because |
|---|---|
| Fixtures as controlled patients in a lab | You need repeatable baseline conditions to trust outcomes |
| Scenario matrix as a flight checklist | Every critical stage must be confirmed before takeoff |
| Hook assertions as circuit breakers | They stop bad states before publication reaches users |
| CLI verification as pre-flight telemetry | It confirms runtime conditions without manual guesswork |
Why It Matters
| Approach | What Happens | Impact in Production |
|---|---|---|
| Deterministic fixtures with golden, partial, and legacy records | Tests reproduce common and edge editorial states | Fewer release surprises and faster incident triage |
| Matrix rows mapped to specific test IDs | Coverage is measurable and auditable | Higher confidence in CI pass signals |
| Hook-level validation tests for publish transitions | Invalid content states are blocked consistently | Better data quality and reduced support load |
| Fixture versioning alongside schema version | Refactors keep tests aligned with model changes | Lower regression risk during migrations |
| Ad hoc hand-made content for testing only (wrong pattern) | Test state drifts between machines and runs | Flaky pipelines and unreliable quality gates |
Reference Table
| Term/API | Signature/Syntax | Purpose | Key Notes |
|---|---|---|---|
wp_insert_post() | `wp_insert_post(array $postarr, bool $wp_error = false, bool $fire_after_hooks = true): int | WP_Error` | Create fixture posts for test packs |
update_field() | `update_field(string $selector, mixed $value, int | string $post_id = false): int | bool` |
get_field() | `get_field(string $selector, int | string $post_id = false, bool $format_value = true): mixed` | Read fixture values for assertions |
acf/validate_value | add_filter('acf/validate_value/name=field_name', callable $callback, int $priority = 10, int $accepted_args = 4): void | Enforce field and cross-field rules | Core validation hook for matrix pass/fail rows |
acf/save_post | add_action('acf/save_post', callable $callback, int $priority = 10, int $accepted_args = 1): void | Enforce publish guard outcomes | Use recursion guard when status is adjusted |
have_rows() | `have_rows(string $selector, int | string $post_id = false): bool` | Iterate Repeater fixtures in tests |
get_sub_field() | get_sub_field(string $selector, bool $format_value = true, bool $escape_html = false): mixed | Validate Repeater sub field content in assertions | ACF Pro Required for Repeater field workflows |
WP_UnitTestCase fixture lifecycle | setUp(): void and tearDown(): void | Prepare and clean fixture state for each test method | Keep tests isolated and deterministic |
wp eval-file | wp eval-file tests/fixtures/seed-script.php | Execute fixture seed scripts in CI/local | Fast validation without browser setup |
Rows using Repeater accessors (have_rows, get_sub_field) depend on ACF Pro field types.
Practical Use Cases
1. Seed deterministic service fixtures for local and CI environments
A healthcare service site has repeated regressions around optional disclaimers, date windows, and CTA fields. The team needs one command that always produces the same fixture records in every environment. Determinism here means stable slugs, stable values, and idempotent upsert behavior.
- Register a CLI command during
acf/initso ACF APIs are guaranteed available. - Upsert fixture pages by slug instead of creating random titles.
- Seed golden, partial, and legacy records with explicit values.
- Include a Repeater fixture set for FAQ content to test Pro field behavior.
- Persist a fixture manifest option with IDs and checksums for audit.
- Run command in local and CI before hook/unit tests.
<?php
declare(strict_types=1);
function clinic_acf_upsert_fixture_page(string $slug, string $title, string $status = 'draft'): int
{
$existing = get_page_by_path($slug, OBJECT, 'page');
if ($existing instanceof WP_Post) {
wp_update_post([
'ID' => (int) $existing->ID,
'post_title' => $title,
'post_status' => $status,
]);
return (int) $existing->ID;
}
return (int) wp_insert_post([
'post_type' => 'page',
'post_status' => $status,
'post_name' => $slug,
'post_title' => $title,
]);
}
add_action('acf/init', function (): void {
if (!defined('WP_CLI') || !WP_CLI || !class_exists('WP_CLI')) {
return;
}
WP_CLI::add_command('clinic-acf fixtures-seed-services', function (array $args, array $assocArgs): void {
$reset = isset($assocArgs['reset']) ? ((int) $assocArgs['reset'] === 1) : false;
$dataset = [
[
'slug' => 'fixture-service-golden',
'title' => 'Fixture Service Golden',
'status' => 'publish',
'fields' => [
'service_headline' => 'Personalized Acne Therapy Program',
'service_intro' => 'Structured treatment pathway with weekly skin response tracking.',
'service_cta_url' => 'https://example.com/book-consultation',
'service_phone' => '+65 6700 8899',
'service_start_date' => '2026-03-01',
'service_end_date' => '2026-06-30',
'service_has_disclaimer' => 1,
'service_disclaimer_text' => 'Consult a licensed physician before treatment.',
'service_faq_items' => [
['service_faq_question' => 'Is this suitable for sensitive skin?', 'service_faq_answer' => 'Yes, protocol is adjusted by skin response.'],
['service_faq_question' => 'How often are sessions scheduled?', 'service_faq_answer' => 'Most plans run every two weeks.'],
],
],
],
[
'slug' => 'fixture-service-partial',
'title' => 'Fixture Service Partial',
'status' => 'draft',
'fields' => [
'service_headline' => 'Partial Content for Validation',
'service_intro' => '',
'service_cta_url' => '',
'service_phone' => '',
'service_start_date' => '2026-05-01',
'service_end_date' => '2026-04-01',
'service_has_disclaimer' => 1,
'service_disclaimer_text' => '',
'service_faq_items' => [],
],
],
[
'slug' => 'fixture-service-legacy',
'title' => 'Fixture Service Legacy Format',
'status' => 'draft',
'fields' => [
'service_headline' => 'Legacy Import Heading',
'service_intro' => 'Imported from pre-2025 CMS export.',
'service_cta_url' => '/legacy-booking',
'service_phone' => '(65)67008899',
'service_start_date' => '2025-11-01',
'service_end_date' => '2026-01-15',
'service_has_disclaimer' => 0,
'service_disclaimer_text' => '',
'service_faq_items' => [
['service_faq_question' => 'Legacy FAQ', 'service_faq_answer' => 'Legacy answer format retained for compatibility tests.'],
],
],
],
];
$manifest = [];
foreach ($dataset as $record) {
$postId = clinic_acf_upsert_fixture_page($record['slug'], $record['title'], $record['status']);
if ($reset) {
delete_post_meta($postId, '_fixture_profile');
delete_post_meta($postId, '_fixture_checksum');
}
foreach ($record['fields'] as $fieldName => $fieldValue) {
update_field($fieldName, $fieldValue, $postId);
}
$checksum = hash('sha256', wp_json_encode($record['fields']));
update_post_meta($postId, '_fixture_profile', $record['slug']);
update_post_meta($postId, '_fixture_checksum', $checksum);
$manifest[] = [
'slug' => $record['slug'],
'post_id' => $postId,
'checksum' => $checksum,
];
}
update_option('clinic_acf_fixture_manifest_services', $manifest, false);
WP_CLI::log('seeded=' . count($manifest));
WP_CLI::log('manifest_hash=' . hash('sha256', wp_json_encode($manifest)));
WP_CLI::success('Service fixtures seeded.');
});
});
wp clinic-acf fixtures-seed-services --reset=1
wp option get clinic_acf_fixture_manifest_services --format=json
wp eval '$id=(int)get_page_by_path("fixture-service-golden", OBJECT, "page")->ID; var_export(get_field("service_headline", $id)); echo PHP_EOL;'
seeded=3
manifest_hash=31f3e8ff41c34c8be12f4f80d8de2bc95b5a7c8ec6502d64fa411d4d8f9f87d1
Success: Service fixtures seeded.
[{"slug":"fixture-service-golden","post_id":1204,"checksum":"d8f1f1a4de7f217ff833f2f4e7e482643f39dce40a5ea3733c3de15264274e6c"},{"slug":"fixture-service-partial","post_id":1205,"checksum":"9d45f91aacbc2c6a6d8fdf8fbdbdc6f8859e84d17c0f625d4f835fca28589a74"},{"slug":"fixture-service-legacy","post_id":1206,"checksum":"5d4e85117fd1212e7439b0158fe556153b2dc7817fc3c1b277e0f7165ca9e964"}]
'Personalized Acne Therapy Program'
Never use randomly generated slugs or titles in baseline fixtures. Random fixtures break traceability between scenario matrix rows and automated tests.
2. Build and run a scenario matrix for validation and publish guard flows
The same service model has date rules and disclaimer requirements that must hold across draft and publish transitions.
A scenario matrix should verify both acf/validate_value outcomes and final post status after acf/save_post.
This use case turns matrix rows into executable checks.
- Add name-targeted validation for
service_end_daterelative toservice_start_date. - Add publish guard that demotes to draft when disclaimer is required but missing.
- Create a CLI command that runs matrix rows against real hook calls.
- Persist matrix results in an option for CI artifact capture.
- Fail pipeline if any row does not match expected result.
<?php
declare(strict_types=1);
add_filter('acf/validate_value/name=service_end_date', function ($valid, $value, $field, $input) {
if ($valid !== true) {
return $valid;
}
$startRaw = '';
if (isset($_POST['acf']) && is_array($_POST['acf'])) {
$startRaw = (string) ($_POST['acf']['field_service_start_date'] ?? '');
}
$endRaw = (string) $value;
if ($startRaw === '' || $endRaw === '') {
return true;
}
$startTs = strtotime($startRaw);
$endTs = strtotime($endRaw);
if ($startTs === false || $endTs === false) {
return 'Start and end dates must be valid date values.';
}
if ($endTs < $startTs) {
return 'Service end date must be later than service start date.';
}
return true;
}, 10, 4);
add_action('acf/save_post', function ($post_id): void {
if (!is_numeric($post_id)) {
return;
}
static $guardLock = false;
if ($guardLock) {
return;
}
$postId = (int) $post_id;
$post = get_post($postId);
if (!$post instanceof WP_Post || $post->post_type !== 'page') {
return;
}
if ($post->post_status !== 'publish') {
return;
}
$requiresDisclaimer = (int) get_field('service_has_disclaimer', $postId) === 1;
$disclaimer = trim((string) get_field('service_disclaimer_text', $postId));
if (!$requiresDisclaimer || $disclaimer !== '') {
return;
}
$guardLock = true;
wp_update_post([
'ID' => $postId,
'post_status' => 'draft',
]);
update_post_meta($postId, '_service_publish_guard_reason', 'missing_disclaimer');
$guardLock = false;
}, 30);
add_action('acf/init', function (): void {
if (!defined('WP_CLI') || !WP_CLI || !class_exists('WP_CLI')) {
return;
}
WP_CLI::add_command('clinic-acf matrix-run-service-release', function (array $args, array $assocArgs): void {
$scenarios = [
[
'id' => 'SRV-001',
'slug' => 'fixture-matrix-valid-publish',
'start' => '2026-04-01',
'end' => '2026-04-30',
'has_disclaimer' => 1,
'disclaimer' => 'Doctor review required before treatment.',
'initial_status' => 'publish',
'expected_validation' => true,
'expected_status' => 'publish',
],
[
'id' => 'SRV-002',
'slug' => 'fixture-matrix-invalid-date',
'start' => '2026-05-20',
'end' => '2026-05-01',
'has_disclaimer' => 1,
'disclaimer' => 'Doctor review required before treatment.',
'initial_status' => 'publish',
'expected_validation' => 'Service end date must be later than service start date.',
'expected_status' => 'publish',
],
[
'id' => 'SRV-003',
'slug' => 'fixture-matrix-missing-disclaimer',
'start' => '2026-06-01',
'end' => '2026-06-20',
'has_disclaimer' => 1,
'disclaimer' => '',
'initial_status' => 'publish',
'expected_validation' => true,
'expected_status' => 'draft',
],
[
'id' => 'SRV-004',
'slug' => 'fixture-matrix-draft-allowed',
'start' => '2026-07-01',
'end' => '2026-07-15',
'has_disclaimer' => 1,
'disclaimer' => '',
'initial_status' => 'draft',
'expected_validation' => true,
'expected_status' => 'draft',
],
];
$results = [];
$passed = 0;
foreach ($scenarios as $scenario) {
$postId = clinic_acf_upsert_fixture_page($scenario['slug'], 'Fixture Matrix ' . $scenario['id'], $scenario['initial_status']);
update_field('service_start_date', $scenario['start'], $postId);
update_field('service_has_disclaimer', $scenario['has_disclaimer'], $postId);
update_field('service_disclaimer_text', $scenario['disclaimer'], $postId);
$_POST['acf'] = [
'field_service_start_date' => $scenario['start'],
];
$validation = apply_filters(
'acf/validate_value/name=service_end_date',
true,
$scenario['end'],
['name' => 'service_end_date'],
'acf[field_service_end_date]'
);
if ($validation === true) {
update_field('service_end_date', $scenario['end'], $postId);
}
wp_update_post([
'ID' => $postId,
'post_status' => $scenario['initial_status'],
]);
do_action('acf/save_post', $postId);
$status = (string) get_post($postId)->post_status;
$validationMatch = $validation === $scenario['expected_validation'];
$statusMatch = $status === $scenario['expected_status'];
$rowPassed = $validationMatch && $statusMatch;
if ($rowPassed) {
$passed++;
}
$results[] = [
'id' => $scenario['id'],
'post_id' => $postId,
'validation_result' => $validation,
'final_status' => $status,
'passed' => $rowPassed,
];
}
update_option('clinic_acf_matrix_report_service_release', $results, false);
WP_CLI::log('rows=' . count($results));
WP_CLI::log('passed=' . $passed);
WP_CLI::log('failed=' . (count($results) - $passed));
WP_CLI::success('Service release matrix complete.');
});
});
wp clinic-acf matrix-run-service-release
wp option get clinic_acf_matrix_report_service_release --format=json
wp eval '$rows=get_option("clinic_acf_matrix_report_service_release", []); $failed=array_filter($rows, static fn($r) => $r["passed"] !== true); echo "failed_rows=" . count($failed) . PHP_EOL;'
rows=4
passed=4
failed=0
Success: Service release matrix complete.
[{"id":"SRV-001","post_id":1301,"validation_result":true,"final_status":"publish","passed":true},{"id":"SRV-002","post_id":1302,"validation_result":"Service end date must be later than service start date.","final_status":"publish","passed":true},{"id":"SRV-003","post_id":1303,"validation_result":true,"final_status":"draft","passed":true},{"id":"SRV-004","post_id":1304,"validation_result":true,"final_status":"draft","passed":true}]
failed_rows=0
Keep matrix IDs stable across releases. Stable IDs let you trace regressions by scenario rather than by changing prose descriptions.
3. Edge case: random fixture generation creates flaky CI results
A team used random fixture titles and values so every run looked slightly different. As soon as tests started asserting sorted output or expected IDs, CI became flaky. The fix is deterministic fixture generation with stable slugs and a dataset hash.
Fragile Pattern (Bad)
<?php
declare(strict_types=1);
add_action('acf/init', function (): void {
if (!defined('WP_CLI') || !WP_CLI || !class_exists('WP_CLI')) {
return;
}
WP_CLI::add_command('clinic-acf fixtures-seed-random', function (): void {
$title = 'Fixture Random ' . wp_rand(1000, 9999);
$postId = (int) wp_insert_post([
'post_type' => 'page',
'post_status' => 'draft',
'post_title' => $title,
]);
update_field('service_phone', '+65 ' . wp_rand(1000, 9999) . ' ' . wp_rand(1000, 9999), $postId);
WP_CLI::success('Created random fixture ' . $postId . '.');
});
});
Robust Pattern (Good)
<?php
declare(strict_types=1);
function clinic_acf_upsert_fixture_by_slug(string $slug, string $title): int
{
$existing = get_page_by_path($slug, OBJECT, 'page');
if ($existing instanceof WP_Post) {
wp_update_post([
'ID' => (int) $existing->ID,
'post_title' => $title,
'post_status' => 'draft',
]);
return (int) $existing->ID;
}
return (int) wp_insert_post([
'post_type' => 'page',
'post_status' => 'draft',
'post_name' => $slug,
'post_title' => $title,
]);
}
add_action('acf/init', function (): void {
if (!defined('WP_CLI') || !WP_CLI || !class_exists('WP_CLI')) {
return;
}
WP_CLI::add_command('clinic-acf fixtures-seed-deterministic', function (array $args, array $assocArgs): void {
$reset = isset($assocArgs['reset']) ? ((int) $assocArgs['reset'] === 1) : false;
$dataset = [
[
'slug' => 'fixture-deterministic-alpha',
'title' => 'Fixture Deterministic Alpha',
'fields' => [
'service_headline' => 'Deterministic Alpha Heading',
'service_phone' => '+65 6700 8899',
'service_start_date' => '2026-08-01',
'service_end_date' => '2026-08-21',
],
],
[
'slug' => 'fixture-deterministic-beta',
'title' => 'Fixture Deterministic Beta',
'fields' => [
'service_headline' => 'Deterministic Beta Heading',
'service_phone' => '+65 6700 8811',
'service_start_date' => '2026-09-01',
'service_end_date' => '2026-09-20',
],
],
];
$created = [];
foreach ($dataset as $record) {
$postId = clinic_acf_upsert_fixture_by_slug($record['slug'], $record['title']);
if ($reset) {
delete_post_meta($postId, '_fixture_deterministic_hash');
}
foreach ($record['fields'] as $fieldName => $fieldValue) {
update_field($fieldName, $fieldValue, $postId);
}
$hash = hash('sha256', wp_json_encode($record));
update_post_meta($postId, '_fixture_deterministic_hash', $hash);
$created[] = [
'slug' => $record['slug'],
'post_id' => $postId,
'hash' => $hash,
];
}
$datasetHash = hash('sha256', wp_json_encode($dataset));
update_option('clinic_acf_fixture_deterministic_manifest', $created, false);
WP_CLI::log('dataset_hash=' . $datasetHash);
WP_CLI::log('records=' . count($created));
WP_CLI::success('Deterministic fixtures seeded.');
});
});
wp clinic-acf fixtures-seed-random
wp clinic-acf fixtures-seed-random
wp clinic-acf fixtures-seed-deterministic --reset=1
wp clinic-acf fixtures-seed-deterministic --reset=1
Success: Created random fixture 1412.
Success: Created random fixture 1413.
dataset_hash=9cbe1a516788801efc1a2d27dbf4c1782a17573431f238f70971bbdb359f5fe1
records=2
Success: Deterministic fixtures seeded.
dataset_hash=9cbe1a516788801efc1a2d27dbf4c1782a17573431f238f70971bbdb359f5fe1
records=2
Success: Deterministic fixtures seeded.
Flaky fixtures create false confidence in passing tests and false alarms in failing tests. Deterministic fixture hashes make this problem visible immediately.
Common Mistakes
| Mistake | Root Cause | What Breaks in Production | Correct Pattern |
|---|---|---|---|
| Only seeding happy-path records | Fixture planning ignores partial and legacy states | Publish-time validation failures appear after release | Run wp clinic-acf fixtures-seed-services --reset=1 with golden, partial, and legacy profiles |
| Randomized fixture values in CI | Team optimizes for speed over repeatability | Flaky tests and unstable snapshots | Use stable slugs and verify with wp option get clinic_acf_fixture_deterministic_manifest --format=json |
| Matrix rows not linked to tests | Scenario docs are disconnected from code | Coverage gaps remain hidden | Store matrix output via update_option('clinic_acf_matrix_report_service_release', $results, false); |
| No publish guard assertion in matrix | Validation-only testing strategy | Content status regressions pass unnoticed | Run wp clinic-acf matrix-run-service-release and assert final_status |
| Fixture scripts not idempotent | Scripts always create new posts | Data bloat and inconsistent test references | Upsert by slug with get_page_by_path() and wp_update_post() |
| Legacy format not represented | Migration compatibility ignored | Old records break in render and export paths | Seed one legacy profile and inspect with wp eval 'var_export(get_field("service_phone", (int)get_page_by_path("fixture-service-legacy", OBJECT, "page")->ID));' |
Deep Dive: Why Random Fixture Bugs Are Harder to Debug Than They Look
Random fixtures make test failures appear non-reproducible because every rerun changes input values. Even when code is correct, output snapshots and sorted lists shift, so teams spend time chasing noise. This problem grows in CI where parallel jobs run with different random seeds. The fix is deterministic input plus a manifest hash that proves fixture identity across runs. Once the hash is stable, a test failure is much more likely to represent a real regression.
wp eval '$manifest=get_option("clinic_acf_fixture_deterministic_manifest", []); echo hash("sha256", wp_json_encode($manifest)) . PHP_EOL;'
Best Practices
- Keep fixture slugs stable and verify lookup with
wp eval '$id=(int)get_page_by_path("fixture-service-golden", OBJECT, "page")->ID; echo $id . PHP_EOL;'. - Add one legacy-format profile per content model and confirm with
wp eval '$id=(int)get_page_by_path("fixture-service-legacy", OBJECT, "page")->ID; var_export(get_field("service_cta_url", $id)); echo PHP_EOL;'. - Use idempotent upsert functions (
get_page_by_path,wp_update_post) instead of always creating new posts. - Track fixture manifest checksums and assert with
wp option get clinic_acf_fixture_manifest_services --format=json. - Execute matrix checks in CI with
wp clinic-acf matrix-run-service-releaseand fail pipeline on non-zero failed rows. - Include publish and draft transition rows in every matrix, not only validation rows.
- Store matrix report artifacts using
update_option('clinic_acf_matrix_report_service_release', $results, false);so failures can be audited.
Hands-On Practice
Exercise 1: Seed baseline service fixtures
Run:
wp clinic-acf fixtures-seed-services --reset=1
wp post list --post_type=page --search="Fixture Service" --fields=ID,post_name,post_status --format=table
After completing this exercise, output should include these slugs:
fixture-service-golden
fixture-service-partial
fixture-service-legacy
Exercise 2: Verify deterministic manifest and checksums
Run:
wp option get clinic_acf_fixture_manifest_services --format=json
After completing this exercise, output should show three objects with keys:
slug
post_id
checksum
Exercise 3: Execute scenario matrix command
Run:
wp clinic-acf matrix-run-service-release
After completing this exercise, output should include:
rows=4
passed=4
failed=0
Exercise 4: Inspect one failing-mode matrix row manually
Run:
wp eval '$rows=get_option("clinic_acf_matrix_report_service_release", []); foreach ($rows as $row) { if ($row["id"] === "SRV-003") { print_r($row); } }'
After completing this exercise, output should include:
[id] => SRV-003
[final_status] => draft
[passed] => 1
Exercise 5: Compare random and deterministic fixture behavior
Run:
wp clinic-acf fixtures-seed-random
wp clinic-acf fixtures-seed-random
wp clinic-acf fixtures-seed-deterministic --reset=1
wp clinic-acf fixtures-seed-deterministic --reset=1
After completing this exercise, output should show different random post IDs but identical deterministic dataset_hash values on repeated deterministic runs.
CLI Reference
| Command | Purpose | Real Example Output |
|---|---|---|
wp clinic-acf fixtures-seed-services --reset=1 | Seed deterministic golden, partial, and legacy fixture profiles | seeded=3 and Success: Service fixtures seeded. |
wp option get clinic_acf_fixture_manifest_services --format=json | Inspect fixture manifest IDs and checksums | [{"slug":"fixture-service-golden","post_id":1204,"checksum":"d8f1f1a4de7f217ff833f2f4e7e482643f39dce40a5ea3733c3de15264274e6c"}] |
wp clinic-acf matrix-run-service-release | Execute matrix assertions for validation and publish guard | rows=4 passed=4 failed=0 |
wp option get clinic_acf_matrix_report_service_release --format=json | Read full scenario matrix result payload | [{"id":"SRV-001","post_id":1301,"validation_result":true,"final_status":"publish","passed":true}] |
wp eval '$rows=get_option("clinic_acf_matrix_report_service_release", []); $failed=array_filter($rows, static fn($r) => $r["passed"] !== true); echo count($failed) . PHP_EOL;' | Count failed matrix rows for CI gate | 0 |
wp eval '$id=(int)get_page_by_path("fixture-service-golden", OBJECT, "page")->ID; var_export(get_field("service_headline", $id)); echo PHP_EOL;' | Confirm golden fixture field value | 'Personalized Acne Therapy Program' |
wp clinic-acf fixtures-seed-random | Demonstrate fragile non-deterministic fixture command | Success: Created random fixture 1412. |
wp clinic-acf fixtures-seed-deterministic --reset=1 | Seed deterministic fixture set with stable hash | dataset_hash=9cbe1a516788801efc1a2d27dbf4c1782a17573431f238f70971bbdb359f5fe1 |
wp eval '$manifest=get_option("clinic_acf_fixture_deterministic_manifest", []); echo hash("sha256", wp_json_encode($manifest)) . PHP_EOL;' | Validate deterministic manifest identity | c91d3cbfd9a8bd708f4054f06052208e0fc4aa4dfb9722d82b0d2e7f0e7aa278 |
What's Next
- Continue to Automated Hook Tests with WP_UnitTestCase.
- Return to Module 9 Overview to keep fixture strategy aligned with full CI gates.
- Related lesson: Understanding ACF Hook Lifecycle and Priorities.
- Related lesson: validate_value, Sanitization, and Publish Guards.
Revisit this lesson whenever you add or rename critical fields, because fixture drift is usually the first cause of false confidence in a passing test suite.