Laravel Example

Testing & TDD

Learn how to write and run different types of tests in Laravel

Unit Testing

Testing individual components

Basic Test Case

// tests/Unit/UserTest.php
class UserTest extends TestCase
{
    /** @test */
    public function it_can_get_full_name()
    {
        $user = new User([
            'first_name' => 'John',
            'last_name' => 'Doe'
        ]);

        $this->assertEquals(
            'John Doe',
            $user->getFullName()
        );
    }

    /** @test */
    public function it_can_check_if_is_admin()
    {
        $user = User::factory()->create([
            'role' => 'admin'
        ]);

        $this->assertTrue($user->isAdmin());
    }
}

Running Tests

# Run all tests
php artisan test

# Run specific test file
php artisan test tests/Unit/UserTest.php

# Run specific test method
php artisan test --filter=it_can_get_full_name

Key Points

  • Test individual components in isolation
  • Use factory() for test data
  • Use assertions for result verification
  • Write descriptive method names

Feature Testing

Testing application features

HTTP Tests

// tests/Feature/PostTest.php
class PostTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function user_can_create_a_post()
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)
            ->post('/posts', [
                'title' => 'My First Post',
                'content' => 'Post content here'
            ]);

        $response->assertRedirect('/posts');
        
        $this->assertDatabaseHas('posts', [
            'title' => 'My First Post',
            'user_id' => $user->id
        ]);
    }

    /** @test */
    public function guests_cannot_create_posts()
    {
        $response = $this->post('/posts', [
            'title' => 'My Post',
            'content' => 'Content'
        ]);

        $response->assertRedirect('/login');
        $this->assertDatabaseMissing('posts', [
            'title' => 'My Post'
        ]);
    }
}

Key Points

  • Test features from user perspective
  • Simulate HTTP requests
  • Verify database changes
  • Test different user roles

Database Testing

Testing database operations

Factory & Seeder Tests

// tests/Unit/Database/PostFactoryTest.php
class PostFactoryTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function it_creates_post_with_valid_data()
    {
        $post = Post::factory()
            ->has(Comment::factory()->count(3))
            ->create([
                'status' => 'published'
            ]);

        $this->assertNotNull($post->title);
        $this->assertEquals('published', $post->status);
        $this->assertCount(3, $post->comments);
    }

    /** @test */
    public function it_seeds_database_correctly()
    {
        $this->seed(PostSeeder::class);

        $this->assertDatabaseCount('posts', 10);
        $this->assertDatabaseHas('posts', [
            'status' => 'published'
        ]);
    }
}

Key Points

  • Test model factories and seeders
  • Verify relationships
  • Test cascade deletes
  • Use RefreshDatabase trait

Mocking

Testing with mock objects

Service Mocking

/** @test */
public function it_sends_welcome_email()
{
    Mail::fake();

    $user = User::factory()->create();

    Mail::assertNothingSent();

    $this->post('/welcome', [
        'user_id' => $user->id
    ]);

    Mail::assertSent(WelcomeEmail::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
}

/** @test */
public function it_processes_payment()
{
    $this->mock(PaymentGateway::class, function ($mock) {
        $mock->shouldReceive('charge')
            ->once()
            ->with(100, 'token')
            ->andReturn([
                'success' => true,
                'transaction_id' => 'tx_123'
            ]);
    });

    $response = $this->post('/payments', [
        'amount' => 100,
        'token' => 'token'
    ]);

    $response->assertSuccessful();
    $this->assertDatabaseHas('payments', [
        'transaction_id' => 'tx_123',
        'status' => 'completed'
    ]);
}

Key Points

  • Mock external services and dependencies
  • Test events and jobs
  • Verify method calls and parameters
  • Use facades for testing