Back to Course |
Testing in Laravel 11 For Beginners

Auth Test: Is User Logged In?

Now, let's see how we can test the users and the access of logged-in users.


First, we must add a Middleware auth to the route.

routes/web.php:

Route::get('/', function () {
return view('home');
})->name('home');
 
Route::resource('products', ProductController::class)->middleware('auth');

After running the tests, we have three failed Feature tests. The two passed tests are Unit tests.

auth tests failed


Fixing Tests: Acting as a User

We need to create a user using Factories for each test function and perform the server request using that user.

For that, there is a method called actingAs(), which accepts the User Model as a parameter.

tests/Feature/ProductsTest.php:

use function Pest\Laravel\actingAs;
 
test('homepage contains empty table', function () {
$user = User::factory()->create();
 
actingAs($user)
->get('/products')
->assertStatus(200)
->assertSee(__('No products found'));
});
 
test('homepage contains non empty table', function () {
$user = User::factory()->create();
$product = Product::create([
'name' => 'Product 1',
'price' => 123,
]);
 
actingAs($user)
->get('/products')
->assertStatus(200)
->assertDontSee(__('No products found'))
->assertSee('Product 1')
->assertViewHas('products', function (LengthAwarePaginator $collection) use ($product) {
return $collection->contains($product);
});
});
 
test('paginated products table doesnt contain 11th record', function () {
$user = User::factory()->create();
$products = Product::factory(11)->create();
$lastProduct = $products->last();
 
actingAs($user)
->get('/products')
->assertStatus(200)
->assertViewHas('products', function (LengthAwarePaginator $collection) use ($lastProduct) {
return $collection->doesntContain($lastProduct);
});
});

Now we have all tests green again.

auth tests passed


Opposite Tests: No Access for Guests

Until now, we have tested one scenario in which logged-in users can access the page. We also need to test the opposite case: unauthenticated users cannot access this page.

For this, let's create a general Auth test with a few methods.

php artisan make:test AuthTest

Notice: you can structure your test files however you want, but it's a common practice to separate files by "topic" like AuthTest, or by features like RegistrationTest, with one or more methods inside each test file.

In this test class, we need to simulate going to the /products page without any user and assert two things:

  • that the status code is 302
  • and that the user is redirected to the /login URL.

tests/Feature/AuthTest.php:

use function Pest\Laravel\get;
 
test('unauthenticated user cannot access product', function () {
get('/products')
->assertStatus(302)
->assertRedirect('login');
});

Now, we have six tests passed.

guest cannon access test

In the final test for this lesson, we will test a login form. Also, after successful login, we need to test that the user is redirected to the products page.

In this test, first, we must create a new user. Then, instead of a GET request, we will send a POST request and pass parameters as an array as in the form. Finally, assert the response status and redirect URL.

tests/Feature/AuthTest.php:

// ...
 
test('login redirects to products', function () {
User::create([
'name' => 'User',
'email' => 'user@user.com',
'password' => bcrypt('password123')
]);
 
post('/login', [
'email' => 'user@user.com',
'password' => 'password123'
])
->assertStatus(302)
->assertRedirect('products');
});

By default, Breeze redirects to the /dashboard URL after successful authentication. We must change its value in the AuthenticatedSessionController.

app/Http/Controllers/Auth/AuthenticatedSessionController.php:

class AuthenticatedSessionController extends Controller
{
// ...
 
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();
 
$request->session()->regenerate();
 
return redirect()->intended(route('dashboard', absolute: false));
return redirect()->intended(route('products.index', absolute: false));
}
 
// ...
}

And we have seven green tests.

login redirects to products test


PHPUnit examples

With PHPUnit, we have identical syntax but with the usage of $this.

tests/Feature/ProductsTest.php:

use App\Models\User;
 
class ProductsTest extends TestCase
{
use RefreshDatabase;
 
public function test_homepage_contains_empty_table(): void
{
$user = User::factory()->create();
 
$response = $this->get('/products');
$response = $this->actingAs($user)->get('/products');
 
$response->assertStatus(200);
$response->assertSee(__('No products found'));
}
 
public function test_homepage_contains_non_empty_table(): void
{
$product = Product::create([
'name' => 'Product 1',
'price' => 123,
]);
 
$user = User::factory()->create();
 
$response = $this->get('/products');
$response = $this->actingAs($user)->get('/products');
 
$response->assertStatus(200);
$response->assertDontSee(__('No products found'));
$response->assertSee('Product 1');
$response->assertViewHas('products', function (LengthAwarePaginator $collection) use ($product) {
return $collection->contains($product);
});
}
 
public function test_paginated_products_table_doesnt_contain_11th_record()
{
$products = Product::factory(11)->create();
$lastProduct = $products->last();
 
$user = User::factory()->create();
 
$response = $this->get('/products');
$response = $this->actingAs($user)->get('/products');
 
$response->assertStatus(200);
$response->assertViewHas('products', function (LengthAwarePaginator $collection) use ($lastProduct) {
return $collection->doesntContain($lastProduct);
});
}
}

Test to check if guests cannot access products:

tests/Feature/AuthTest.php:

class AuthTest extends TestCase
{
public function test_unauthenticated_user_cannot_access_product()
{
$response = $this->get('/products');
 
$response->assertStatus(302);
$response->assertRedirect('login');
}
}

The last test for authentication uses the POST method instead of GET.

tests/Feature/AuthTest.php:

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
 
class AuthTest extends TestCase
{
use RefreshDatabase;
 
public function test_login_redirects_to_products()
{
User::create([
'name' => 'User',
'email' => 'user@user.com',
'password' => bcrypt('password123')
]);
 
$response = $this->post('/login', [
'email' => 'user@user.com',
'password' => 'password123'
]);
 
$response->assertStatus(302);
$response->assertRedirect('products');
}
 
// ...
}

So, in short, if you want to test the authenticated user access, use actingAs() with a specific User model value.