Appearance
date: 2026-06-16 tags: [pest, testing, expectation, footgun] status: active graduated_to:
Pest higher-order expect($model)->value->toBe(...) breaks — value collides with the Expectation's own property
Symptom — a model assertion written as a higher-order chain fails with Error: Call to undefined method App\Models\TaskHelperContext::toBe(), even though the property exists and the value is correct. Surfaced in MemoriesControllerTest on a model with a value column (PR #516).
Root cause — Pest's Expectation object exposes a public $value property (the wrapped value). Higher-order property access (->name, ->category) only proxies into a sub-expectation for undefined properties; ->value resolves to the Expectation's own property instead, returning the raw model. The following ->toBe(...) is then called on the model, which has no such method. Any attribute whose name shadows an Expectation property (value is the common one) hits this.
Fix — assert the property directly rather than through the chain:
php
// ✗ breaks — ->value returns the model, not a sub-expectation
expect($model)->value->toBe('x')->category->toBe('y');
// ✓ assert the attribute outside the chain
expect($model->value)->toBe('x');
expect($model->category)->toBe('y');See tests/Feature/MemoriesControllerTest.php (the "stores a new memory" and "updates a memory" cases).
Guard — convention only: when a model has a value (or otherwise Expectation-shadowing) attribute, assert it with expect($model->value), never expect($model)->value. No automated check; if it recurs, graduate the one-liner into .claude/rules/testing.md.