Back to Course |
Testing in Laravel 11 For Beginners

Edit Product: Correct Data in the Form?

The next obvious thing we would build in such a project is editing the product. So let's write the code and the tests for the edit form.


Laravel Code

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) {{-- [tl! add:start] --}}
<th class="px-6 py-3 bg-gray-50 text-left">
</th>
@endif {{-- [tl! add:end] --}}
</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) {{-- [tl! add:start] --}}
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<a href="{{ route('products.edit', $product) }}" class="...">
Edit
</a>
</td>
@endif {{-- [tl! add:end] --}}
</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>
// ...

products table with edit button

When we go to the Edit page, the values in the inputs will be tested by the test method we will create in a minute.

edit product form


The 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 the first assert that the status is 200.

tests/Feature/ProductsTest.php:

// ...
 
test('product edit contains correct values', function () {
$product = Product::factory()->create();
 
asAdmin()->get('products/' . $product->id . '/edit')
->assertStatus(200);
});

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:

// ...
 
test('product edit contains correct values', function () {
$product = Product::factory()->create();
 
asAdmin()->get('products/' . $product->id . '/edit')
->assertStatus(200)
->assertSee('value="' . $product->name . '"', false)
->assertSee('value="' . $product->price . '"', false);
});

And let's add another assertion to check that View has variable $product with the same values.

tests/Feature/ProductsTest.php:

// ...
 
test('product edit contains correct values', function () {
$product = Product::factory()->create();
 
asAdmin()->get('products/' . $product->id . '/edit')
->assertStatus(200)
->assertSee('value="' . $product->name . '"', false)
->assertSee('value="' . $product->price . '"', false)
->assertViewHas('product', $product);
});

product edit form test


PHPUnit examples

Here, we have the same test for PHPUnit.

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,
]);
}
}