Testing#
ACF Chef keeps field definitions close to plain PHP, which makes them straightforward to test without booting WordPress or connecting to a database.
Use the compiled array when you care about exactly what ACF will receive. Use FieldNode::describe() when you care about the shape of the definition before compilation.
Reset static state between tests#
Chef holds three swappable collaborators behind interfaces:
- The field registry
- The compiler
- The key strategy
You can replace them with:
Chef::setRegistry($registry);
Chef::setCompiler($compiler);
Chef::keyStrategy($strategy);
keyStrategy() acts as both getter and setter.
Call Chef::reset() between tests:
protected function tearDown(): void
{
Chef::reset();
parent::tearDown();
}
reset() drops all three collaborators. The next use builds fresh defaults.
Static state that survives one test can make the next test depend on execution order, especially when tests register custom verbs, replace the compiler or change the global key strategy.
Use an isolated compiler#
You can construct a compiler with its own collaborators:
use AcfChef\Compiler;
use AcfChef\KeyStrategy\NamespacedKeys;
$compiler = new Compiler(
$registry,
new NamespacedKeys(),
);
A compiler given both its registry and key strategy does not need the corresponding global Chef state.
Anything you leave out is resolved from Chef when compilation runs rather than when the compiler is constructed.
A strategy passed directly to compile() has the highest precedence, overriding both the compiler’s configured strategy and the global one.
Compilation is repeatable#
extended-acf keeps generated keys in process-global state. Without isolation, serialising the same definition more than once can produce its duplicate-key error.
ACF Chef isolates that store for each compilation.
You can therefore compile the same field group repeatedly:
$first = $group->toArray();
$second = $group->toArray();
$this->assertSame($first, $second);
The same applies when a group is registered more than once during a test.
Each compilation builds fresh field objects and produces the same final array.
Assert on the definition tree#
When the exact ACF array is not what you are testing, FieldNode::describe() gives you a plain-array description of a node and its subtree.
It returns exactly six keys:
[
'verb' => ...,
'name' => ...,
'config' => ...,
'conditionals' => ...,
'deferred' => ...,
'children' => ...,
]
This is useful for testing recipes, path edits or other describe-phase behaviour without coupling the assertion to serialisation details.
name is the declared name#
describe() reports the name used in the definition, not the compiled field name.
For example:
$group->tab('Content');
describes the node name as:
Content
not:
content_tab
The marker suffix is a compile-time field-name concern.
deferred is a count#
Deferred configuration closures are not placed into the description array.
Instead, deferred is the number of stored closures:
$group->text('title')
->config(fn ($field) => $field->required())
->config(fn ($field) => $field->maxLength(60));
describes with:
'deferred' => 2
That keeps the description plain, readable and suitable for assertions.
Shorthands are already expanded#
The config entry reflects describe-phase configuration after shorthand expansion.
With this shorthand registered:
Chef::shorthand('half', fn (): array => ['wrapper' => ['width' => 50]]);
$group->text('title')->config([
'half' => true,
]);
is described with configuration equivalent to:
[
'wrapper' => [
'width' => 50,
],
]
rather than retaining a half key.
Assert against the expansion, not the shorthand — and remember that a test which resets Chef between cases has to register the shorthand again.
WordPress is not required for definition tests#
A field-group definition does not need a WordPress bootstrap or database merely to be built and inspected.
Tests only need stubs for the small WordPress boundary reached by whichever operation they exercise.
See Why ACF Chef for that boundary and How it works for the distinction between describing and compiling a definition.