A new fluent testing API for Sharp 9.16.0
Feb 2, 2026 by Philippe Lonchampt
We're excited to announce that Sharp 9.16.10 brings a completely redesigned testing API that makes writing tests for your Sharp applications more intuitive and enjoyable. After spending considerable time writing tests for Sharp itself, we realized we could do much better than the legacy testing helpers. So we rebuilt the testing experience from the ground up.
Getting started with the new API
First, include the SharpAssertions trait in your test case. Whether you're using PHPUnit or Pest, it's simple to set up:
PHPUnit:
use Code16\Sharp\Utils\Testing\SharpAssertions;
abstract class TestCase extends BaseTestCase
{
use SharpAssertions;
// ...
}
Pest:
use Code16\Sharp\Utils\Testing\SharpAssertions;
pest()
->extend(\Tests\TestCase::class)
->use(SharpAssertions::class);
How to test Entity Lists?
The new API makes testing Entity Lists straightforward:
it('allows the user to access the post list', function () {
$user = User::factory()->create();
$this
->loginAsSharpUser($user)
->sharpList(Post::class)
->get()
->assertOk()
->assertListData(fn (AssertableJson $data) => $data
->count(3)
->has('0.title', 'My first post')
->etc()
);
});
Need to test filtered results? Simply chain withFilter():
$this->sharpList(Post::class)
->withFilter(CategoryFilter::class, 1)
->get()
->assertOk();
Testing Commands is now a pleasure
Commands are a core part of Sharp, and are also the part which needs tests. The new API makes it much easier than before:
$this->sharpList(Post::class)
->entityCommand(ExportPosts::class)
->getForm()
->assertFormData(fn (AssertableJson $data) => $data
->where('format', 'xls') // Assert initial data
->etc()
)
->post(['format' => 'csv'])
->assertReturnsDownload('posts.csv');
Testing multi-step commands (wizards) used to be particularly cumbersome… but not anymore! Check out the new syntax:
$this->sharpList(Post::class)
->entityCommand(EvaluatePostWizardCommand::class)
->getForm()
->post(['decision' => 'rejected'])
->assertReturnsStep('reason')
->getNextStepForm()
->assertFormData(fn (AssertableJson $data) => $data
->where('reviewer_name', $user->name)
->etc()
)
->post(['reason' => 'some refusal reason'])
->assertOk();
Write tests for a complex context with stacked Show Pages
Show Pages follow the same fluent pattern:
$this->sharpShow(Post::class, 1)
->get()
->assertOk()
->assertShowData(fn (AssertableJson $data) => $data
->where('title', 'My first post')
->where('author', 'John Doe')
->etc()
);
The real benefit from the new API is that it is now easy to test nested List fields:
$this->sharpShow(Post::class, 1)
->sharpListField(Comment::class)
->get()
->assertOk()
->assertListData(fn (AssertableJson $data) => $data
->count(5)
);
And for complex scenarios with stacked shows, just chain your calls. Say you want to test a command placed on the Show Page of a Comment, which is attached to a particular Post:
$this->sharpList(Post::class)
->sharpShow(Post::class, 1)
->sharpListField(Comment::class)
->sharpShow(Comment::class, 1)
->instanceCommand(ModerateComment::class, 1)
->post()
->assertOk();
Notice that you don't have to handle manually the breadcrumb to specify the context.
And more...
Forms are of course fully testable, as well as Dashboards: check the full documentation to learn more on this, but I bet that you won't even need it since the whole API is consistent and discoverable through IDE autocomplete.
We're continuing to improve Sharp's developer experience, and this new testing API is just one part of that journey. As always, we welcome your feedback and contributions. If you run into any issues or have ideas for improvements, don't hesitate to open an issue on GitHub.
Happy testing!