Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel View Test Assertions Laravel Package

jcergolj/laravel-view-test-assertions

A Laravel testing package that adds expressive assertions for Blade views, letting you verify rendered output, view data, and components directly in PHPUnit or Pest tests with clean, readable methods instead of brittle checks.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a micro-package (16 stars, single-author) that layers a handful of extra view assertions on top of Laravel’s TestResponse. Expect it to be small, so the first move is to read the source, not the README — with a package this size the entire API is usually one trait plus a few protected methods.

Install and wire up

composer require jcergolj/laravel-view-test-assertions --dev

Most packages of this shape ship a trait you use in your base TestCase rather than a service provider:

// tests/TestCase.php
use JCergolj\ViewTestAssertions\ViewTestAssertions;

abstract class TestCase extends BaseTestCase
{
    use ViewTestAssertions;
}

If there is no trait, look for a class exposing static helpers or a macro registration in a service provider listed under extra.laravel.providers in composer.json.

Where to look first

  1. src/ — enumerate the public methods. That is your API surface.
  2. tests/ in the package — the author’s own assertions are the best usage documentation.
  3. composer.json — check the require block for the minimum Laravel/PHPUnit versions; this determines whether the package is even viable on your stack.

First use case

The value proposition is collapsing multi-line, brittle view assertions into one expressive call. The typical starting point is asserting a view was rendered and carries the data you expect:

public function test_invoice_page_renders_with_totals(): void
{
    $response = $this->actingAs($user)->get('/invoices/1');

    $response->assertOk();
    $response->assertViewIs('invoices.show');
    $response->assertViewHasAll(['invoice', 'lineItems']);
}

Swap that last line for whatever the package actually exposes (a "has all / has only / has count / has missing" family is the usual pattern). If Laravel already covers the case in your installed version, you do not need the package for it — see Gotchas.

Implementation Patterns

Feature tests, not unit tests

The assertions operate on TestResponse, so they belong in HTTP/feature tests that go through the router and controller. For tests that call view() directly, you get a View instance, not a TestResponse, and these assertions generally will not apply. Keep a separate set of plain PHPUnit assertions for view-unit tests.

Data assertions vs. rendered-output assertions

Keep the two lanes distinct in your tests; it makes failures readable:

// Data plane: what the controller handed the view
$response->assertViewHas('invoice');

// Render plane: what the user sees
$response->assertSeeText('Invoice #1234');
$response->assertSeeInOrder(['Subtotal', 'Tax', 'Total']);

Use the package’s data assertions to replace long chains of assertViewHas('x', fn ($v) => ...) closures, and its render assertions for anything that requires the Blade output to be correct.

Standard arrange/act/assert skeleton

$response = $this->actingAs($user)->get(route('dashboard'));

$response->assertOk();
$response->assertViewIs('dashboard.index');
$response->assertViewHas('stats');
$response->assertViewHas('stats', fn ($stats) => $stats['open'] === 3);
$response->assertSeeText('3 open tickets');

Adopting the package here is a pure readability win: one intent per line, each with a distinct failure message.

Reducing duplication with a local helper

If your app renders the same shell (layout, navigation, breadcrumbs) across many pages, wrap the invariant checks:

private function assertAdminShell(TestResponse $response): void
{
    $response->assertViewHas('navigation');
    $response->assertSeeText('Admin');
    $response->assertSee(route('admin.settings'));
}

Call it from every admin feature test. This is where small assertion packages earn their keep — they make your own helpers shorter.

Integration with data providers

View assertions pair well with PHPUnit data providers for content permutations:

#[DataProvider('planProvider')]
public function test_plan_label_renders(string $plan, string $expected): void
{
    $response = $this->actingAs(User::factory()->create(['plan' => $plan]))->get('/billing');

    $response->assertSeeText($expected);
}

Because each assertion produces a specific message, a failing provider case tells you exactly which permutation broke.

CI and parallel testing

Nothing here is stateful or container-bound beyond the trait, so these assertions are safe under phpunit --parallel and in GitHub Actions matrices. Just pin the package version in composer.lock; with a 16-star package, an unpinned minor bump is a real risk.

Gotchas and Tips

Overlap with built-in Laravel assertions

Laravel has been steadily absorbing view assertions (assertViewHas, assertViewHasAll, assertViewMissing, assertSeeInOrder, assertSeeTextInOrder, assertViewHasAll). Before writing a test, check the Laravel docs for your version. If the package defines a method with the same name and signature, the trait will shadow it in your TestCase — usually harmless, but a behavioral difference in edge cases (null handling, closure arguments) will be invisible until a test mysteriously passes or fails.

Data is not HTML

assertViewHas('errors') checks the view’s data bag. assertSee('...') checks the rendered string. A view can receive an empty errors collection and still render a red banner from a global share, or receive data it never renders. Assert the plane you actually care about; do not mix them to "cover both" in a single expectation.

Escaping and whitespace

assertSee escapes by default; use the false second argument or assertSeeText deliberately. assertSeeText strips tags, which can make an assertion pass that a user would never see rendered (e.g., text inside a hidden <div>). If exact markup matters, assert on assertSee with raw HTML and be prepared to normalize whitespace — Blade’s indentation changes with every refactor.

Missing vs. null

assertViewHas('foo') typically passes when foo exists as null. If the distinction between "key absent" and "key is null" matters to your controller contract, test both explicitly and inspect how the package implements the check before trusting it.

Collections, models, and lazy values

View data is often an Eloquent Collection or a paginator. Comparing with assertViewHas('users', $expectedArray) fails on identity, not content. Resolve first ($users->toArray()) or use a closure that inspects the object. Watch out for LazyCollection — some assertions that count or iterate will trigger the query inside the assertion, turning a fast test into a slow one, or into a failure if the DB connection is already torn down.

Debugging a failing assertion

Drop into the actual payload before guessing:

dump($response->viewData());      // the data bag
dump($response->getContent());   // the rendered HTML

That pair resolves 90% of "why did this assertion fail" questions in view tests. Also check $response->exception / $response->status() — a 500 with a rendered error page can still contain the string you are asserting on.

Version pinning and maintenance

The lone-version-tag + tiny-star-count profile means you should treat upgrades as untested. Pin to an exact version, run your suite, and eyeball the diff of src/ before bumping. If the package goes quiet, the assertions are trivial enough to vendor into a tests/Concerns/ViewAssertions.php trait — consider doing that pre-emptively for the two or three methods you actually use, rather than taking a dependency for the whole set.

Extension points

If the package exposes macros or a trait, that is your seam. Add project-specific assertions (tenant scoping, feature-flag visibility) as methods on your own trait that composes theirs, so the third-party surface stays one use line and is easy to replace later.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource