Back to Course |
Testing in Laravel 11 For Beginners

Delete Product Testing

The final part of testing the CRUD of products is, of course, the delete action.


Laravel Code

Again, first, the code for the Controller, Route, and delete button.

app/Http/Controllers/ProductController.php:

use Illuminate\Http\RedirectResponse;
 
class ProductController extends Controller
{
// ...
 
public function destroy(Product $product): RedirectResponse
{
$product->delete();
 
return redirect()->route('products.index');
}
}

routes/web.php:

use App\Http\Controllers\ProductController;
 
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');
Route::delete('products/{product}', [ProductController::class, 'destroy'])->name('products.destroy');
});
});

resources/views/products/index.blade.php:

// ...
@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="...">
Edit
</a>
<form action="{{ route('products.destroy', $product) }}" method="POST" class="inline-block">
@csrf
@method('DELETE')
<x-primary-button onclick="return confirm('Are you sure?')" class="bg-red-600 text-white">Delete</x-primary-button>
</form>
</td>
@endif
// ...

After visiting the /products URL, we now see a Delete button, which is working.

products table delete button


The Test

In the test, we will simulate the DELETE request and assert the database records.

The start of this test is the same as with the previous test. We create a product using Factory and make the delete request acting as an admin user. The first two assertions are the same: the status code is 302, and the redirect URL is back to /products.

tests/Feature/ProductsTest.php:

// ...
 
test('product delete successful', function () {
$product = Product::factory()->create();
 
asAdmin()
->delete('products/' . $product->id)
->assertStatus(302)
->assertRedirect('products');
});

In the previous lesson, we had assertDatabaseHas. In this case, there is the opposite assertion called assertDatabaseMissing where the first parameter is the table and the second the array of values.

Also, let's count the database records, with assertDatabaseCount(). In the beginning, the table is empty, then we create one record and delete it. So, at the end of the test, the table should be empty.

tests/Feature/ProductsTest.php:

// ...
 
test('product delete successful', function () {
$product = Product::factory()->create();
 
asAdmin()
->delete('products/' . $product->id)
->assertStatus(302)
->assertRedirect('products');
 
$this->assertDatabaseMissing('products', $product->toArray());
$this->assertDatabaseCount('products', 0);
});

These two database assertions can be made with different methods. For assertDatabaseMissing(), the alternative assertModelMissing could be used. And for assertDatabaseCount(), the assertDatabaseEmpty() could be used in this particular case:

tests/Feature/ProductsTest.php:

// ...
 
test('product delete successful', function () {
$product = Product::factory()->create();
 
asAdmin()
->delete('products/' . $product->id)
->assertStatus(302)
->assertRedirect('products');
 
$this->assertDatabaseMissing('products', $product->toArray());
$this->assertDatabaseCount('products', 0);
 
$this->assertModelMissing($product);
$this->assertDatabaseEmpty('products');
});

test product delete


PHPUnit example

Here, we have the same test for PHPUnit.

tests/Feature/ProductsTest.php:

class ProductsTest extends TestCase
{
// ...
 
public function test_product_delete_successful()
{
$product = Product::factory()->create();
 
$response = $this->actingAs($this->admin)->delete('products/' . $product->id);
 
$response->assertStatus(302);
$response->assertRedirect('products');
 
$this->assertDatabaseMissing('products', $product->toArray());
$this->assertDatabaseCount('products', 0);
 
$this->assertModelMissing($product);
$this->assertDatabaseEmpty('products');
}
 
private function createUser(bool $isAdmin = false): User
{
return User::factory()->create([
'is_admin' => $isAdmin,
]);
}
}