The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

Testing Length Validation in Laravel

Published on by

Testing Length Validation in Laravel image

I thought it might help people new to the Laravel framework and testing, to walk through how to test length validation. When I say length validation, I mean the constraints of length that you might want to put on a string field.

For example, let’s say that we wanted to limit the length of a user’s real name to 50 characters; or if we restrict the email address to the database column length of 255. Along with the database constraints, we should add validation constraints to the controller when creating and updating records.

When testing lengths, there are a couple of techniques I think can be helpful that I use with HTTP tests to verify that my validation is working as expected. Let’s walk through some hands-on examples of the validation portion of a controller request; we will also need a test database to demonstrate a test that interacts with a database.

Getting Started

Laravel comes with a User model and a factory to go along with the model, so that’s what we’ll use to demonstrate our testing techniques.

First, let’s create a sample Laravel project:

laravel new length-validation
cd length-validation

Next, let’s create a controller and a test case to demonstrate how I like to test length validation:

php artisan make:controller UsersController
php artisan make:test UserTest

The last part of our setup is defines the POST route for creating new users in routes/web.php:

Route::post('/users', 'UsersController@store');

I will leave it up to you to set up a testing database. If you want to use an SQLite in-memory database, update your phpunit.xml file to the following:

<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>

Setting Up Validation

Our first task is setting up validation in the controller. When validation succeeds we are just going to return a simple JSON response, however, you might be redirecting the user or doing something else after creating the record. We’ll keep it simple though, so we can focus on how to test length validation:

<?php
 
namespace App\Http\Controllers;
 
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
 
class UsersController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'name' => 'required|max:50',
'email' => 'required|email|max:255',
'password' => 'required',
]);
 
 
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

We’re using the max validation rule to impose a maximum length value on our fields, which we are ready to test!

Testing Max Length Validation

First, let’s look at how to test to ensure that validation fails when our fields are over the max value. We are not double-checking that the Laravel max validation rule works properly here, our purpose is defining business requirements for length, that when changed, should trigger failed tests. If we remove the max requirement in the future, the tests act as a safety-harness to remind us of the business rules associated with the data.

Let’s open up the (potentially poorly named) tests/Feature/UserTest.php file and write our first test:

<?php
 
namespace Tests\Feature;
 
use App\User;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
 
class UserTest extends TestCase
{
use RefreshDatabase;
 
public function setUp()
{
parent::setUp();
 
$this->user = factory(User::class)->make();
}
 
/** @test */
function name_should_not_be_too_long()
{
$response = $this->post('/users', [
'name' => str_repeat('a', 51),
'email' => $this->user->email,
'password' => 'secret',
]);
 
$response->assertStatus(302);
$response->assertSessionHasErrors([
'name' => 'The name may not be greater than 50 characters.'
]);
}
}

Our first test makes a request to the /users endpoint with a name that is just outside the boundary of our max rule using PHP’s str_repeat() function. We rely on the email address field from a new User instance we are creating before each test runs.

By default, validation will redirect the user when validation fails, so we check for a 302 status code and verify that our validation error is in the session.

Testing the Bounds of Validation

Although not always critical, I like to test the inner bounds of length validation, ensuring that a field that is just the right amount of length passes:

/** @test */
function name_is_just_long_enough_to_pass()
{
$response = $this->post('/users', [
'name' => str_repeat('a', 50),
'email' => $this->user->email,
'password' => 'secret',
]);
 
$this->assertDatabaseHas('users', [
'email' => $this->user->email,
]);
$response->assertStatus(201);
}

This test ensures that a name that is just the right length and that the request will still end up storing the user in the database.

Testing the Email Address

The email address length validation tests are almost identical, but I wanted to show you the slight difference:

/** @test */
function email_should_not_be_too_long()
{
$response = $this->post('/users', [
'name' => $this->user->name,
'email' => str_repeat('a', 247).'@test.com', // 256
]);
 
$response->assertStatus(302);
$response->assertSessionHasErrors([
'email' => 'The email may not be greater than 255 characters.'
]);
}

In this case, we take the difference of @test.com and repeat a valid email character that makes a total string length of 256 characters.

Here’s an example of the email string sent:

php artisan tinker
str_repeat('a', 247).'@test.com'
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@test.com"
>>> strlen(str_repeat('a', 247).'@test.com')
=> 256

Testing Length Validation Rules in Separate Tests

You might be asking: “why don’t we just test all of the length validation rules in one test?” You could definitely do that, but I feel that the separation allows you to focus isolating the single validation rule on an otherwise valid request.

The test reads a little clearer too, so I think the extra testing verbosity reads more clearly to developers that might be reading the tests. For example, the test “name_should_not_be_too_long” is much clearer that something like “test_validation_rules.”

“Between” Validation Tests

When you’re testing validation between two values, you want to test the lower and upper bounds of the validation rule. For example, let’s say we wanted our name field to be between 10 characters and 50:

$data = $request->validate([
'name' => 'required|between:10,50',
'email' => 'required|email|max:255',
'password' => 'required',
]);

In this case, you might want to test the following conditions:

  • when a name is just too short
  • when the name is just the minimum amount required
  • when the name is just the maximum amount required
  • when the name is too long

You might feel confident only testing too short or too long scenarios, but I think it’s worth showing you how to check scenarios when the field is valid but on the edge of the constraints. You will get a feel for when you need to test every situation, and usually just checking the “too long” or “too short” is good enough!

Testing Multiple Cases

I think it’s worth bringing up that validation testing is a good place to try out multiple cases of valid or invalid data.

Let’s say that you needed to write your own email validation rule with custom requirements, and you want to ensure that the validation rules work in a variety of cases.

You could make the decision to unit test a custom rule, but the principle is the same: loop through multiple cases and makes assertions on each.

/** @test */
function email_validation_should_reject_invalid_emails()
{
collect(['you@example,com', 'bad_user.org', 'example@bad+user.com'])->each(function ($invalidEmail) {
$this->post('/users', [
'name' => $this->user->name,
'email' => $invalidEmail,
'password' => 'secret',
])->assertSessionHasErrors([
'email' => 'The email must be a valid email address.'
]);
});
}

Closing Thoughts

In this beginner testing tutorial, I showed you how I go about testing validation boundaries for string lengths. The string length validation might be to match up with a database column length, ensuring the data meets a business requirement, or both.

String columns typically should have some validation length that associates with the database column type (i.e. varchar(255)), and testing the max length allowed ensures that your controllers validate strings before they get inserted into the database improperly.

I also showed you an example of how writing a test for each validation scenario might make tests clearer to read by singling out each validation rule’s requirements.

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Bacancy

Outsource a dedicated Laravel developer for $3,200/month. With over a decade of experience in Laravel development, we deliver fast, high-quality, and cost-effective solutions at affordable rates.

Visit Bacancy
Curotec logo

Curotec

World class Laravel experts with GenAI dev skills. LATAM-based, embedded engineers that ship fast, communicate clearly, and elevate your product. No bloat, no BS.

Curotec
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $3200/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
Clawdbot Rebrands to Moltbot After Trademark Request From Anthropic image

Clawdbot Rebrands to Moltbot After Trademark Request From Anthropic

Read article
Automate Laravel Herd Worktrees with This Claude Code Skill image

Automate Laravel Herd Worktrees with This Claude Code Skill

Read article
Laravel Boost v2.0 Released with Skills Support image

Laravel Boost v2.0 Released with Skills Support

Read article
Laravel Debugbar v4.0.0 is released image

Laravel Debugbar v4.0.0 is released

Read article
Radiance: Generate Deterministic Mesh Gradient Avatars in PHP image

Radiance: Generate Deterministic Mesh Gradient Avatars in PHP

Read article
Speeding Up Laravel News With Cloudflare image

Speeding Up Laravel News With Cloudflare

Read article