The final part of testing the CRUD of products is, of course, the delete action.
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.
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'); });
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, ]); }}