Might be related to all the headers you've added to the request.
Try this:
$response = $this->get('/some-route')
->assertInertia(fn (AssertableInertia $page) =>
// ...
);
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
assertInertia() throws a Not a valid Inertia response error when simulating an Inertia.js visit like so:
$response = $this
->withHeaders([
"accept" => "text/html, application/xhtml+xml",
"content-type" => "application/json",
"x-inertia" => "true",
"x-inertia-partial-component" => "Post/Edit",
"x-inertia-partial-data" => "post",
"x-inertia-version" => "eaa52f64811e388602bb56eca093f022",
"x-requested-with" => "XMLHttpRequest",
])
->get('/some-route')
->assertInertia(fn (AssertableInertia $page) =>
// ...
);
How do you test this type of request?
Might be related to all the headers you've added to the request.
Try this:
$response = $this->get('/some-route')
->assertInertia(fn (AssertableInertia $page) =>
// ...
);
@gych thank you for your reply!
The reason for the headers is to emulate an Inertia.js visit, since I have a middleware that checks for $request>inertia().
I think when I add the "x-inertia" => "true" header to make my middleware pass, the Inertia::render('some/page') doesn't actually return a page, so this method from AssertableInertia.php does not pass:
public static function fromTestResponse(TestResponse $response): self
{
try {
$response->assertViewHas('page');
$page = json_decode(json_encode($response->viewData('page')), true);
PHPUnit::assertIsArray($page);
PHPUnit::assertArrayHasKey('component', $page);
PHPUnit::assertArrayHasKey('props', $page);
PHPUnit::assertArrayHasKey('url', $page);
PHPUnit::assertArrayHasKey('version', $page);
} catch (AssertionFailedError $e) {
PHPUnit::fail('Not a valid Inertia response.');
}
$instance = static::fromArray($page['props']);
$instance->component = $page['component'];
$instance->url = $page['url'];
$instance->version = $page['version'];
return $instance;
}
In particular, the $response->assertViewHas('page'); line.
@ignaciodev What is returned then as response when this fails ? Also for what is the middleware exactly used?
The controller is returning:
Inertia::render("Post/Edit", ['post' => $post ]);
Due to some (complex) logic of the app I'm building, if the request is not an Inertia.js partial visit coming from a particular page, my middleware will redirect to posts.show route.
It's all working great, my only issue comes from the automated tests.
The headers I have added have been copied from a dd($request), to ensure they are identical to when I make the request by clicking on my UI.
The response's assertOk() is passing.
The $this->get() on my test returns a Illuminate\Testing\TestResponse with a 200 status code.
@ignaciodev Ok and for the test, does the middleware recognize the request with the headers you've added as Inertia request ?
@gych yes! The code execution makes it all the way to the Inertia::render without issues, and the response code is 200.
The middleware (which passes without issues) is checking it like this:
if (
$request->inertia() &&
$request->header('x-inertia-partial-component') === 'Post/Show'
) {
return $next($request);
}
@ignaciodev When you debug the response which data does it contain?
$response = $this
->withHeaders([
"accept" => "text/html, application/xhtml+xml",
"content-type" => "application/json",
"x-inertia" => "true",
"x-inertia-partial-component" => "Post/Edit",
"x-inertia-partial-data" => "post",
"x-inertia-version" => "eaa52f64811e388602bb56eca093f022",
"x-requested-with" => "XMLHttpRequest",
])
->get('/some-route');
dd($response);
$response->assertInertia(fn (AssertableInertia $page) =>
// ...
);
@gych this is what the dd($response) is showing:
Illuminate\Testing\TestResponse^ {#2324 // tests/Feature/PepeTest.php:44
+baseResponse: Illuminate\Http\JsonResponse^ {#2233
+headers: Symfony\Component\HttpFoundation\ResponseHeaderBag^ {#2248
#headers: array:5 [
"x-inertia" => array:1 [
0 => "true"
]
"cache-control" => array:1 [
0 => "no-cache, private"
]
"date" => array:1 [
0 => "Tue, 23 Apr 2024 13:39:37 GMT"
]
"content-type" => array:1 [
0 => "application/json"
]
"vary" => array:1 [
0 => "X-Inertia"
]
]
#cacheControl: []
#computedCacheControl: array:2 [
"no-cache" => true
"private" => true
]
#cookies: array:1 [
"" => array:1 [
"/" => array:2 [
"XSRF-TOKEN" => Symfony\Component\HttpFoundation\Cookie^ {#2284
#name: "XSRF-TOKEN"
#value: "eyJpdiI6IjJCWFZwSEd1aGdvRUZJSUd6Vm1reHc9PSIsInZhbHVlIjoia3BpUTJiM00va1AwejlKZkE2QVFWV3FEQXVIYi9LQlovaEtxVVJ0WWg3U3pHYTY3YWFqVGo0aksxMEt4WFFSL2REWjFVcGt4Qld1dDJ5NFcwbXNoRkRtS0tPbFpiUCsyZlVZVHg5RCtHWkhJVmpRbEdUaWpud2xJUkRuR2R6cysiLCJtYWMiOiIxNWQ3ZGE5MjhhYjI3OTczM2IwZGI1ZWI2ZTcxNzc3ZGVkYmI5MDQ4OTI1ZDQyOTg1YjA2MDdkNmZiYzM1MjFhIiwidGFnIjoiIn0="
#domain: null
#expire: 1713886777
#path: "/"
#secure: false
#httpOnly: false
-raw: false
-sameSite: "lax"
-secureDefault: false
}
"my_video_session_session" => Symfony\Component\HttpFoundation\Cookie^ {#2282
#name: "my_video_session_session"
#value: "eyJpdiI6ImQ4ZmtHOEtmSkwwNjRvRG9vL1NpcEE9PSIsInZhbHVlIjoiWU1HSDV3WGIxb21HNkhlWm5kNTFhSWJndUNKeWQvZlR2K1psak9UL0RPUWhMSUI5OFpQZ0VIM08yellMTnVCQVRwU0FReWJYZ01YSlFxUCtOZzRVa1pxMlhMZ2tURyt4alI2dHZTV21IbDU0OTBpc0tEVmtUMTBDaG9tUm5ONFIiLCJtYWMiOiJjMmJhMmRmYzA1YWVhMGViMWJiNWEwOTliMDU2NjdlOGM3YTNlYjYzODA1ZTI5ODM2ODEzMWMzYmJmM2RmYjY1IiwidGFnIjoiIn0="
#domain: null
#expire: 1713886777
#path: "/"
#secure: false
#httpOnly: true
-raw: false
-sameSite: "lax"
-secureDefault: false
}
]
]
]
#headerNames: array:6 [
"x-inertia" => "X-Inertia"
"cache-control" => "Cache-Control"
"date" => "Date"
"content-type" => "Content-Type"
"vary" => "Vary"
"set-cookie" => "Set-Cookie"
]
}
#content: "{"component":"Post\/EditPage","props":[],"url":"\/posts\/YmxkazYeJ0P\/edit","version":"eaa52f64811e388602bb56eca093f022"}"
#version: "1.1"
#statusCode: 200
#statusText: "OK"
#charset: null
#data: "{"component":"Post\/EditPage","props":[],"url":"\/posts\/YmxkazYeJ0P\/edit","version":"eaa52f64811e388602bb56eca093f022"}"
#callback: null
#encodingOptions: 0
+original: array:4 [
"component" => "Post/EditPage"
"props" => []
"url" => "/posts/YmxkazYeJ0P/edit"
"version" => "eaa52f64811e388602bb56eca093f022"
]
+exception: null
}
+exceptions: Illuminate\Testing\LoggedExceptionCollection^ {#857
#items: []
#escapeWhenCastingToString: false
}
#streamedContent: null
}
@ignaciodev Indeed when adding the x-inertia header it doesn't return the page component.
What is it, that you want to achieve by checking that the request is an Inertia request ? For example what should happen if it doesn't pass the middleware?
Please or to participate in this conversation.