The next obvious thing we would build in such a project is editing the product. The edit form should contain the exact values we will test.
Before adding the test, we first need an edit form, routes, and controller methods.
app/Http/Controllers/ProductController.php:
use App\Models\Product;use Illuminate\Contracts\View\View;use Illuminate\Http\RedirectResponse; class ProductController extends Controller{ // ... public function store(StoreProductRequest $request): RedirectResponse { Product::create($request->validated()); return redirect()->route('products.index'); } public function edit(Product $product): View { return view('products.edit', compact('product')); }}
routes/web.php:
Route::middleware('auth')->group(function () { Route::get('products', [ProductController::class, 'index'])->name('products.index'); Route::middleware('is_admin')->group(function () { Route::get('products/create', [ProductController::class, 'create'])->name('products.create'); Route::post('products', [ProductController::class, 'store'])->name('products.store'); Route::get('products/{product}/edit', [ProductController::class, 'edit'])->name('products.edit'); Route::put('products/{product}', [ProductController::class, 'update'])->name('products.update'); });});
We will test the product update in the next lesson, but the routes must be prepared now because we must use it in the form.
resources/views/products/edit.blade.php:
<x-app-layout> <x-slot name="header"> <h2 class="font-semibold text-xl text-gray-800 leading-tight"> {{ __('Edit product') }} </h2> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="overflow-hidden overflow-x-auto p-6 bg-white border-b border-gray-200"> <div class="min-w-full align-middle"> <form method="POST" action="{{ route('products.update', $product) }}"> @csrf @method('PUT') <div> <x-input-label for="name" :value="__('Name')" /> <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="$product->name" required /> <x-input-error :messages="$errors->get('name')" class="mt-2" /> </div> <div class="mt-4"> <x-input-label for="price" :value="__('Price')" /> <x-text-input id="price" class="block mt-1 w-full" type="text" name="price" :value="$product->price" required /> <x-input-error :messages="$errors->get('price')" class="mt-2" /> </div> <div class="flex items-center mt-4"> <x-primary-button> {{ __('Save') }} </x-primary-button> </div> </form> </div> </div> </div> </div> </div></x-app-layout>
resources/views/products/index.blade.php:
// ...<table class="min-w-full divide-y divide-gray-200 border"> <thead> <tr> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Name</span> </th> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Price (USD)</span> </th> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Price (EUR)</span> </th> @if (auth()->user()->is_admin) <th class="px-6 py-3 bg-gray-50 text-left"> </th> @endif </tr> </thead> <tbody class="bg-white divide-y divide-gray-200 divide-solid"> @forelse($products as $product) <tr class="bg-white"> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ $product->name }} </td> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ number_format($product->price, 2) }} </td> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ $product->price_eur }} </td> @if (auth()->user()->is_admin) <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> <a href="{{ route('products.edit', $product) }}" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150"> Edit </a> </td> @endif </tr> @empty <tr class="bg-white"> <td colspan="2" class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ __('No products found') }} </td> </tr> @endforelse </tbody></table>// ...
When we go to the edit page, the values in the inputs will be tested.
Now, let's create a test. In the test, we will first use the Factory to create a product and assign it to a variable. Then acting as admin, we will visit the edit page for that product and, from the response, will make a first assert that the status is 200.
tests/Feature/ProductsTest.php:
class ProductsTest extends TestCase{ // ... public function test_product_edit_contains_correct_values() { $product = Product::factory()->create(); $response = $this->actingAs($this->admin)->get('products/' . $product->id . '/edit'); $response->assertStatus(200); } private function createUser(bool $isAdmin = false): User { return User::factory()->create([ 'is_admin' => $isAdmin, ]); }}
Next, because the response returns the HTML, we can use assertSee
to assert that there is an input with a product name and price value. We must add a second parameter as false
so that quotes won't be escaped and show as special chars.
tests/Feature/ProductsTest.php:
class ProductsTest extends TestCase{ // ... public function test_product_edit_contains_correct_values() { $product = Product::factory()->create(); $response = $this->actingAs($this->admin)->get('products/' . $product->id . '/edit'); $response->assertStatus(200); $response->assertSee('value="' . $product->name . '"', false); $response->assertSee('value="' . $product->price . '"', false); } private function createUser(bool $isAdmin = false): User { return User::factory()->create([ 'is_admin' => $isAdmin, ]); }}
And let's add another assertion to check that View has variable product
with the same values.
tests/Feature/ProductsTest.php:
class ProductsTest extends TestCase{ // ... public function test_product_edit_contains_correct_values() { $product = Product::factory()->create(); $response = $this->actingAs($this->admin)->get('products/' . $product->id . '/edit'); $response->assertStatus(200); $response->assertSee('value="' . $product->name . '"', false); $response->assertSee('value="' . $product->price . '"', false); $response->assertViewHas('product', $product); } private function createUser(bool $isAdmin = false): User { return User::factory()->create([ 'is_admin' => $isAdmin, ]); }}