Laravel Unit Testing Best Practices
Introduction
Testing is essential for building reliable Laravel applications. Proper unit testing helps catch bugs early, ensures code quality, and provides confidence when making changes. Laravel provides excellent testing tools built on PHPUnit.
In this guide, we'll explore Laravel testing best practices, covering unit tests, feature tests, database testing, and strategies for building a comprehensive test suite.
Types of Tests
Laravel supports different types of tests:
- Unit Tests - Test individual methods or functions
- Feature Tests - Test larger chunks of functionality
- Browser Tests - Test JavaScript interactions
- API Tests - Test HTTP endpoints
The testing pyramid suggests having many unit tests, fewer feature tests, and even fewer browser tests.
Unit Tests
Create unit tests for individual components:
php artisan make:test Unit/OrderTotalTest
namespace Tests\Unit;
use Tests\TestCase;
use App\Services\OrderTotalCalculator;
class OrderTotalTest extends TestCase
{
public function test_calculates_total_with_tax()
{
$calculator = new OrderTotalCalculator();
$total = $calculator->calculate(100, 0.1);
$this->assertEquals(110, $total);
}
public function test_returns_zero_for_empty_amount()
{
$calculator = new OrderTotalCalculator();
$total = $calculator->calculate(0, 0.1);
$this->assertEquals(0, $total);
}
}
Feature Tests
Test complete features with HTTP requests:
php artisan make:test Feature/CreateProductTest
class CreateProductTest extends TestCase
{
public function test_can_create_product()
{
$response = $this->postJson('/api/products', [
'name' => 'Test Product',
'price' => 99.99,
'description' => 'Test description'
]);
$response->assertStatus(201)
->assertJsonFragment([
'name' => 'Test Product',
'price' => 99.99
]);
}
public function test_validation_fails_for_invalid_data()
{
$response = $this->postJson('/api/products', [
'name' => '',
'price' => -10
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['name', 'price']);
}
}
Testing Databases
Use in-memory databases for fast tests:
// phpunit.xml
class ProductTest extends TestCase
{
use RefreshDatabase;
public function test_can_create_product()
{
$product = Product::create([
'name' => 'Test Product',
'price' => 50.00
]);
$this->assertDatabaseHas('products', [
'name' => 'Test Product'
]);
}
}
Best Practices
- Test one thing - Each test should verify one behavior
- Use descriptive names - Name tests clearly
- Arrange-Act-Assert - Structure tests consistently
- Mock external services - Use fakes and mocks
- Run tests frequently - Integrate into development
- Aim for high coverage - But prioritize critical paths
- Keep tests fast - Fast tests run more often
Summary
Comprehensive testing is crucial for building reliable Laravel applications. By following these best practices, you can create a test suite that catches bugs early and gives confidence in your code.
For more information, check out our other tutorials on Laravel REST API and Laravel Microservices.