You're absolutely right—this is a subtle but important gap in test coverage when using Livewire with TDD. Your HTTP-level (feature) tests only check that the Livewire component is present, not that it's being passed the correct data from the Blade view. Your Livewire component tests, meanwhile, test the component in isolation and don't catch issues with how it's invoked in the Blade template.
The core issue:
If you forget to pass :vehicle="$vehicle" to the Livewire component in your Blade view, your tests still pass, but the app breaks at runtime.
Solution: Test the Blade-to-Livewire Data Passing
1. Feature Test: Assert the Livewire Component Receives the Data
You can write a feature test that renders the page and inspects the rendered HTML to ensure the wire:initial-data attribute (or the hidden input for the property) contains the expected vehicle data. However, Livewire serializes the data, so this can be brittle and is not recommended for complex objects.
Instead, the most robust approach is to test the Blade view directly and assert that the Livewire component is being invoked with the correct parameters.
Example: Blade View Test
If you're using Pest, you can do something like this:
it('passes the vehicle to the Livewire component in the blade view', function () {
$dealership = Dealership::factory()->create();
$vehicle = Vehicle::factory()->for($dealership)->create();
$view = $this->view('pages.dealership.listing', [
'dealership' => $dealership,
'vehicle' => $vehicle,
]);
// Assert that the Livewire component is rendered with the vehicle
$view->assertSee('@livewire(\'create-enquiry\', [\'vehicle\' => $vehicle])');
});
However, this only works if you use the Blade directive with the array syntax. If you use the @livewire('create-enquiry', ['vehicle' => $vehicle]) syntax, you can assert that the rendered view contains the correct Livewire markup.
Alternatively: Rendered HTML Assertion
You can check that the rendered HTML contains the vehicle's data (e.g., the vehicle's title or ID in a hidden input):
it('renders the vehicle data in the Livewire component markup', function () {
$dealership = Dealership::factory()->create();
$vehicle = Vehicle::factory()->for($dealership)->create();
$response = $this->get(route('page.dealership.listing', [
'dealership' => $dealership,
'vehicle' => $vehicle,
]));
$response->assertSee((string) $vehicle->id); // or another unique property
});
2. Recommendation: Always Pass Required Props Explicitly
To avoid this class of bug, always pass required data explicitly:
@livewire('create-enquiry', ['vehicle' => $vehicle])
You can even write a test that fails if you forget to do this, as above.
3. Consider a Custom Blade Component
If you want to enforce this pattern, you could wrap your Livewire component in a custom Blade component that requires the vehicle as a prop, and then always pass it through.
4. E2E Tests
Ultimately, only an E2E test (with Laravel Dusk, Pest Browser, or Cypress) can guarantee the full integration works. But the above strategies can catch most issues earlier.
Summary
- Feature tests can assert the presence of the Livewire component, but not the data passed to it.
- Blade view tests can assert that the Livewire component is invoked with the correct data.
- Rendered HTML tests can check for the presence of the vehicle's data in the markup.
- E2E/browser tests are the only way to guarantee the full integration, but you can catch most issues with careful Blade and feature tests.
TL;DR:
Add a Blade view test to assert that @livewire('create-enquiry', ['vehicle' => $vehicle]) is present in your view. This will fail if you forget to pass the required data, closing the gap in your TDD workflow.
Let me know if you'd like more concrete examples or have follow-up questions!