본문 바로가기

PHP & Laravel

Laravel과 PHP 8에서의 테스트 주도 개발(TDD) 전략

반응형

Laravel과 PHP 8에서의 테스트 주도 개발(TDD) 전략

 

테스트 주도 개발(Test-Driven Development, TDD)은 테스트를 먼저 작성하고, 테스트를 통과할 수 있도록 코드를 작성하는 개발 방법론입니다. Laravel은 강력한 테스트 도구를 제공하며, PHP 8의 새로운 기능과 결합하면 더욱 견고하고 유지보수하기 쉬운 애플리케이션을 구축할 수 있습니다.

 

이 글에서는 Laravel과 PHP 8을 활용한 TDD의 개념, 구현 방법, 그리고 실전 적용 사례를 살펴보겠습니다.

 

1. 테스트 주도 개발(TDD)이란?

 

TDD는 다음과 같은 “Red-Green-Refactor” 사이클을 따릅니다.

1. Red (실패): 실패하는 테스트 작성

2. Green (성공): 테스트를 통과할 수 있도록 최소한의 코드 작성

3. Refactor (리팩토링): 중복 제거 및 코드 개선

 

TDD의 주요 이점:

코드의 신뢰성 증가

리팩토링 시 안정성 보장

개발 속도 향상

 

2. Laravel에서 TDD를 위한 준비

 

Laravel에서는 기본적으로 PHPUnit이 내장되어 있으며, Pest 테스트 프레임워크도 지원합니다.

 

2.1 테스트 환경 설정

 

테스트 실행을 위해 .env.testing 파일을 설정합니다.

 

DB_CONNECTION=sqlite

DB_DATABASE=:memory:

APP_ENV=testing

 

이 설정은 테스트 시 SQLite 인메모리 데이터베이스를 사용하도록 하여 테스트 속도를 크게 향상시킵니다.

 

2.2 테스트 실행 방법

 

테스트를 실행하려면 다음 명령어를 사용합니다.

 

php artisan test        # Pest를 사용할 경우

php artisan test --filter=UserTest  # 특정 테스트만 실행

phpunit                 # PHPUnit 실행

 

3. PHPUnit을 활용한 기본 TDD 예제

 

3.1 테스트 클래스 생성

 

아래 명령어를 통해 사용자 기능을 위한 테스트 클래스를 생성합니다.

 

php artisan make:test UserTest

 

생성된 테스트 파일 tests/Feature/UserTest.php에서 다음과 같이 작성할 수 있습니다.

 

namespace Tests\Feature;



use Tests\TestCase;

use App\Models\User;

use Illuminate\Foundation\Testing\RefreshDatabase;



class UserTest extends TestCase {

    use RefreshDatabase;



    public function test_user_can_be_created() {

        // 실패하는 테스트 작성 (Red)

        $response = $this->postJson('/api/users', [

            'name' => 'John Doe',

            'email' => 'john@example.com',

            'password' => 'password123'

        ]);



        $response->assertStatus(201);

        $this->assertDatabaseHas('users', ['email' => 'john@example.com']);

    }

}

 

설명:

postJson() → API 엔드포인트 테스트

assertStatus(201) → HTTP 응답 상태 코드 확인

assertDatabaseHas() → 데이터베이스에 값 존재 여부 확인

 

3.2 테스트를 통과하도록 코드 작성 (Green)

 

app/Http/Controllers/UserController.php에서 테스트를 통과하도록 최소한의 기능을 구현합니다.

 

public function store(Request $request) {

    $request->validate([

        'name' => 'required|string',

        'email' => 'required|email|unique:users',

        'password' => 'required|min:8'

    ]);



    $user = User::create([

        'name' => $request->name,

        'email' => $request->email,

        'password' => bcrypt($request->password),

    ]);



    return response()->json($user, 201);

}

 

설명:

유효성 검사 추가

사용자 생성 로직 구현

 

테스트를 다시 실행하면 성공(Green)하게 됩니다.

 

3.3 리팩토링 (Refactor)

 

이제 UserController의 코드를 리팩토링하여 서비스 클래스를 도입할 수 있습니다.

 

서비스 클래스 예제:

 

namespace App\Services;



use App\Models\User;



class UserService {

    public function createUser(array $data): User {

        $data['password'] = bcrypt($data['password']);

        return User::create($data);

    }

}

 

컨트롤러에서 서비스 클래스를 주입받아 코드 개선:

 

use App\Services\UserService;



public function store(Request $request, UserService $userService) {

    $user = $userService->createUser($request->all());

    return response()->json($user, 201);

}

4. Pest를 활용한 간결한 테스트 작성

 

Pest는 PHPUnit 기반의 간결하고 직관적인 테스트 프레임워크입니다.

 

4.1 Pest 설치

 

composer require pestphp/pest --dev

php artisan pest:install

 

4.2 Pest 테스트 예제

 

Pest를 사용하면 다음과 같이 간결한 테스트를 작성할 수 있습니다.

 

it('creates a new user', function() {

    $response = $this->postJson('/api/users', [

        'name' => 'Jane Doe',

        'email' => 'jane@example.com',

        'password' => 'password123'

    ]);



    $response->assertStatus(201);

    $this->assertDatabaseHas('users', ['email' => 'jane@example.com']);

});

 

장점:

간결한 문법

높은 가독성

 

5. 테스트 전략 및 모범 사례

 

TDD를 실무에서 성공적으로 적용하려면 다음과 같은 전략을 고려해야 합니다.

 

5.1 단위(Unit) 테스트 vs 기능(Feature) 테스트

단위 테스트: 특정 기능 또는 메서드를 테스트

기능 테스트: API 엔드포인트, 전체 흐름을 테스트

 

예시:

 

public function test_user_email_validation() {

    $response = $this->postJson('/api/users', [

        'name' => 'Alice',

        'email' => 'invalid-email',

        'password' => 'password123'

    ]);



    $response->assertStatus(422);

}

5.2 목(Mocking) 객체 활용

 

데이터베이스 쿼리를 최소화하기 위해 모의(Mock) 객체를 사용할 수 있습니다.

 

use Mockery;

use App\Services\UserService;



public function test_user_service() {

    $userService = Mockery::mock(UserService::class);

    $userService->shouldReceive('createUser')->andReturn(new User(['email' => 'test@example.com']));



    $this->assertEquals('test@example.com', $userService->createUser([])->email);

}

 

5.3 CI/CD 파이프라인에 테스트 자동화 적용

GitHub Actions 또는 GitLab CI를 통해 코드 푸시 시 자동 테스트 실행

.github/workflows/laravel.yml 예제

 

name: Laravel Tests



on: [push, pull_request]



jobs:

  test:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v3

      - name: Install dependencies

        run: composer install

      - name: Run tests

        run: php artisan test

 

6. 결론

 

TDD를 활용하면 Laravel 애플리케이션의 품질과 신뢰성을 높일 수 있습니다. PHP 8의 새로운 기능을 적용하고, Pest 등의 최신 도구를 활용하여 더욱 간결하고 효율적인 테스트 환경을 구축해 보세요.

 

핵심 요약:

TDD의 Red-Green-Refactor 사이클 적용

Laravel의 PHPUnit 및 Pest를 활용한 테스트 자동화

테스트를 CI/CD 파이프라인에 통합하여 품질 보장

 

반응형