In a Laravel feature test, how do you request a sneaker shop's checkout routes and assert the status, redirect and view data?
answer
- $this->get() returns TestResponse
- assertOk means exactly 200
- assertRedirect or assertRedirectToRoute
- assertViewIs and assertViewHas
- failure message shows the last exception
basics
~10 sCall $this->get() or $this->post() inside a Tests\TestCase test; each returns a TestResponse. Chain assertOk() or assertStatus(), assertRedirect() or assertRedirectToRoute(), and assertViewIs() or assertViewHas() for the Blade data.
solid answer
~40 sInside a class extending `Tests\TestCase`, `$this->get('/checkout')` or `$this->post('/checkout', $data)` sends the request through the HTTP kernel in the same PHP process and returns an `Illuminate\Testing\TestResponse`. Assertions chain on it: `assertOk()` for exactly 200, `assertCreated()` for 201, `assertStatus(302)` for any code; `assertRedirect('/orders/42')` or `assertRedirectToRoute('orders.show', $order)` for where a POST sent the user; `assertViewIs('checkout.show')` and `assertViewHas('cart')` for the Blade view and its data. `assertViewHas` takes a value, a closure, or a model it compares with `is()`. When an assertion fails, Laravel appends the last exception or the validation errors to the message, which is usually the whole diagnosis. The docs advise one request per test.
code
php · 33 lines<?php
namespace Tests\Feature;
use App\Models\Order;
use App\Models\User;
use Tests\TestCase;
class CheckoutPageTest extends TestCase
{
public function test_checkout_page_shows_the_cart(): void
{
$user = User::factory()->create();
$this->actingAs($user)
->get('/checkout')
->assertOk()
->assertViewIs('checkout.show')
->assertViewHas('cart')
->assertSee('Air Max 90');
}
public function test_placing_an_order_redirects_to_it(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/checkout', [
'sku' => 'AM90-42', 'size' => 42,
]);
$response->assertRedirectToRoute('orders.show', Order::first());
}
}go deeper
Write $this->get() or post() and chain assertOk, assertRedirect and assertViewHas; know that assertOk means exactly 200.
Explain that the request runs through the kernel in-process, how from() makes redirect-back testable, and how assertViewHas compares models with is().
Use the failure context TestResponse appends to diagnose quickly, prefer named-route redirects, and keep tests on routes rather than controller internals.
Set the team's testing contract for endpoints: which assertions every route needs, one request per test, and route-level tests as the default confidence layer.
## Making the request Feature tests extend `Tests\TestCase`, which boots the application before each test. The base class provides one method per HTTP verb: - `$this->get($uri, $headers)` and `$this->head(...)` - `$this->post($uri, $data, $headers)`, `put`, `patch`, `delete`, `options` - JSON variants such as `getJson` and `postJson`, plus the generic `json($method, $uri, $data)` Each builds a request, hands it to the HTTP kernel **in the same PHP process**, runs the full middleware and routing stack, and returns an `Illuminate\Testing\TestResponse`. No web server is involved. ```php $response = $this->get('/checkout'); ``` ## Asserting the status `TestResponse` carries one assertion per common status code. The names are exact, not ranges: | Assertion | Passes when the status is | |---|---| | `assertOk()` | exactly 200 | | `assertCreated()` | exactly 201 | | `assertNoContent()` | 204 by default | | `assertFound()` | exactly 302 | | `assertNotFound()` / `assertForbidden()` | 404 / 403 | | `assertUnprocessable()` | 422 | | `assertSuccessful()` | any 2xx | | `assertStatus($code)` | the given code | A checkout endpoint that answers 201 therefore fails `assertOk()`; use `assertCreated()` or `assertSuccessful()`. ## Asserting a redirect A classic sneaker-shop checkout form posts, creates an order, and redirects to a confirmation page. Three assertions cover it: 1. `assertRedirect()` with no argument checks the status is one of 201, 301, 302, 303, 307 or 308. 2. `assertRedirect('/orders/42')` also checks the `Location` header. 3. `assertRedirectToRoute('orders.show', $order)` builds the URL from a route name, so the test survives URL changes. To test "redirect back", set where the user came from first: `$this->from('/checkout')->post('/checkout', $data)` stores the previous URL in the session and sends a `Referer` header, so `assertRedirect('/checkout')` has something to match. ## Asserting the view For pages rendered with Blade, the response keeps the original `View` object: - `assertViewIs('checkout.show')` checks the view name. - `assertViewHas('cart')` checks a key exists in the view data. - `assertViewHas('cart', $cart)` compares a value; for an Eloquent model it uses `$model->is($actual)`, which compares primary key, table and connection rather than every attribute. - `assertViewHas('items', fn ($items) => $items->count() === 2)` checks with a closure. - `assertViewMissing('coupon')` checks a key is absent. `assertSee('Air Max 90')` checks the rendered HTML contains text, escaped by default; use it for what the user sees, and `assertViewHas` for what the controller passed. ## Reading failures `TestResponse` wraps PHPUnit's assertions and **adds context from the response** on failure. If the request threw an exception, the failure message includes "The following exception occurred during the last request" and the exception. If validation failed, it lists the errors from the session or the JSON body. So a failing `assertRedirect()` on the checkout usually tells you directly that a `size` field was missing or a `QueryException` was thrown. ## A worked checkout flow Put together, testing the sneaker shop's web checkout usually means three tests, each making one request: 1. **The page.** A signed-in customer requests `GET /checkout`; assert `assertOk()`, `assertViewIs('checkout.show')` and `assertViewHas('cart')`, then `assertSee('Air Max 90')` for the item in the cart. 2. **The happy submit.** The customer posts a valid size and address; assert `assertRedirectToRoute('orders.show', $order)`, so the flow provably ends on the confirmation page. 3. **The failed submit.** The customer posts without a size, coming from the checkout page; assert `assertRedirect('/checkout')` and, with the validation assertions, which field failed. Each test sets up only what its request needs. Keeping the steps apart means a failure names the step that broke, instead of one long test failing somewhere in the middle and leaving you to work out which request went wrong. ## Habits worth naming - **One request per test.** The docs warn that several requests in one test method can behave unexpectedly, because the same application instance serves them all. - **Test through the route.** Assert on what the endpoint returns, not on controller internals; that keeps tests valid through refactors. - **Chain assertions.** Every assertion returns the response, so `->assertOk()->assertViewIs('checkout.show')->assertSee('Checkout')` reads as one statement. - **Prefer named routes** in redirects, so a URL change in `routes/web.php` does not break a dozen tests. A junior candidate is expected to write the request and a status assertion unprompted; interviewers then ask how to check the redirect and the view data, and which assertion fails for a 201.
- A test posting to /checkout fails assertRedirect() with 'received 500'; where do you look first?At the rest of the failure message. `TestResponse` appends the exception thrown during the last request, with its message and trace, when a status assertion fails. That usually names the cause, such as a missing column or an unbound service, without adding any debugging code.
- How do you assert that a failed checkout sends the user back to the form?Set the origin with `$this->from('/checkout')`, which stores the previous URL in the session and sends a `Referer` header, then post and call `assertRedirect('/checkout')`. Without `from()`, redirecting back has no previous URL to go to, and the assertion compares against the wrong location.
saying these in an interview costs you the question
- assertOk() passes for any 2xx status, including 201.
- Laravel feature tests start a real web server and send HTTP over a socket.
- assertViewHas with a model compares every attribute of the two models.
- assertRedirect() with no argument checks the redirect goes to the home page.
- Making many requests in one test method is the recommended style.