Back to Course |
Testing in Laravel 11 For Beginners

First Test: Is Product Table Empty?

Now, let's start writing tests for a simple but real project that would list the products.

empty products table


Initial Project

First, we will make a simple page listing the products. For the frontend, we will be using Laravel Breeze. So first, the Model and Migration.

database/migrations/xxx_create_products_table.php:

public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('price');
$table->timestamps();
});
}

app/Models/Product.php:

class Product extends Model
{
protected $fillable = [
'name',
'price',
];
}

Next, the Controller, View, and Route.

app/Http/Controllers/ProductController.php:

use App\Models\Product;
use Illuminate\Contracts\View\View;
 
class ProductController extends Controller
{
public function index(): View
{
$products = Product::all();
 
return view('products.index', compact('products'));
}
}

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

<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Products') }}
</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">
<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</span>
</th>
</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>
</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>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

routes/web.php:

use App\Http\Controllers\ProductController;
 
Route::get('/', function () {
return view('home');
})->name('home');
 
Route::resource('products', ProductController::class);
 
// ...

Notice: we are using Resource for Route because later, we will add other methods like create(), store(), etc.

After visiting /products, we should see an empty table.

empty products table

Now, let's write tests for this page.


Writing Tests

For the first tests, we will check two scenarios:

  • Whether the page contains the text No products found when there are no records
  • And the opposite: whether this text isn't rendered when the record in the DB exists.

Reminder: We will use Pest PHP testing framework in this course. At the end of the lesson, we will see how the same could be achieved using PHPUnit.

We can create a new test class using an artisan command. It will generate a feature test.

php artisan make:test ProductsTest

In this test, we need to get the /products URL and, from the response, assert if we see the No products found text. The test's name will be homepage contains empty table so that it will be clear what it tests.

tests/Feature/ProductsTest.php:

use function Pest\Laravel\get;
 
test('homepage contains empty table', function () {
get('/products')
->assertStatus(200)
->assertSee(__('No products found'));
});

Instead of using typical $this->get() syntax we used the get() function from the pestphp/pest-plugin-laravel plugin.

If we run the tests, we will see this new test passed successfully.

products table empty test

Let's add another test to assert that the No products found text isn't rendered when the products DB is not empty. Instead of assertSee(), the opposite assertDontSee() method is used.

For now, we will create the product manually inside the test, and later, we will see how to do it automatically in a separate testing database.

tests/Feature/ProductsTest.php:

use App\Models\Product;
use function Pest\Laravel\get;
 
test('homepage contains empty table', function () {
get('/products')
->assertStatus(200)
->assertSee(__('No products found'));
});
 
test('homepage contains non empty table', function () {
Product::create([
'name' => 'Product 1',
'price' => 123,
]);
 
get('/products')
->assertStatus(200)
->assertDontSee(__('No products found'));
});

Run the tests.

products table isn't empty test

So, this is how you write tests to see if some text is/isn't present on the page.


PHPUnit examples

To create a new PHPUnit test class, the --phpunit parameter must be added to the make:test artisan command.

php artisan make:test ProductsTest --phpunit

tests/Feature/ProductsTest.php:

class ProductsTest extends TestCase
{
public function test_homepage_contains_empty_table(): void
{
$response = $this->get('/products');
 
$response->assertStatus(200);
$response->assertSee(__('No products found'));
}
}

The second test to assert that the No products found text isn't rendered.

tests/Feature/ProductsTest.php:

use App\Models\Product;
 
class ProductsTest extends TestCase
{
public function test_homepage_contains_empty_table(): void
{
$response = $this->get('/products');
 
$response->assertStatus(200);
$response->assertSee(__('No products found'));
}
 
public function test_homepage_contains_non_empty_table(): void
{
Product::create([
'name' => 'Product 1',
'price' => 123,
]);
 
$response = $this->get('/products');
 
$response->assertStatus(200);
$response->assertDontSee(__('No products found'));
}
}

Run the tests.

products table isn't empty test


So, we have covered another simple example: simulated both cases in the products table when it is and isn't empty.