개발 지식

개발 지식

PHP ✅ PHP에서 유닛 테스트 가능한 아키텍처 구성법

페이지 정보

profile_image
영삼이
0건 159회 25-03-28 23:26

본문

✅ PHP에서 유닛 테스트 가능한 아키텍처 구성법

많은 PHP 프로젝트가 테스트를 "나중에" 하려고 하다가 결국 테스트 불가능한 구조가 되는 경우가 많습니다. 테스트를 잘 하기 위해선 테스트 코드보다 먼저, 테스트 가능한 구조를 설계하는 것이 핵심입니다. 이 글에서는 PHP에서 유닛 테스트를 쉽게 할 수 있는 아키텍처 설계 팁을 정리합니다.


🧱 1. 비즈니스 로직과 프레임워크 코드 분리

[code=php]
// ❌ 잘못된 예: 컨트롤러에 로직 다 때려넣음
class UserController {
    public function register(Request $request) {
        $pdo = new PDO(...); // DB 종속
        ...
    }
}
[/code]
[code=php]
// ✅ 좋은 예: 서비스에 로직 위임
class UserService {
    public function register(string $email, string $name): void {
        ...
    }
}

class UserController {
    public function register(Request $request, UserService $service) {
        $service->register($request->get('email'), $request->get('name'));
    }
}
[/code]
  • 비즈니스 로직은 서비스 클래스에 위치

  • 프레임워크 의존성이 낮아지고, 테스트하기 쉬워짐


🧩 2. 의존성은 생성자 주입으로 처리

[code=php]
class UserService {
    public function __construct(private UserRepository $repo) {}

    public function register(string $email, string $name): void {
        $this->repo->save(new User($email, $name));
    }
}
[/code]
  • 의존성을 new로 직접 만들지 않고, DI 컨테이너 또는 테스트 코드에서 주입

  • Mock이나 Fake 주입 가능


✅ 3. 외부 의존성은 인터페이스로 추상화

[code=php]
interface Mailer {
    public function send(string $to, string $msg): void;
}
[/code]

```php
[code=php]
class SmtpMailer implements Mailer {
    public function send(string $to, string $msg): void {
        // 실제 메일 발송
    }
}
[/code]
[code=php]
class FakeMailer implements Mailer {
    public array $sent = [];

    public function send(string $to, string $msg): void {
        $this->sent[] = [$to, $msg];
    }
}
[/code]
  • 실 서비스와 테스트를 동일한 구조로 유지하면서도 독립적인 테스트 가능


🧪 4. PHPUnit 테스트 예시

[code=php]
class UserServiceTest extends \PHPUnit\Framework\TestCase {
    public function testRegisterSavesUser() {
        $repo = $this->createMock(UserRepository::class);
        $repo->expects($this->once())
             ->method('save');

        $service = new UserService($repo);
        $service->register('kim@example.com', 'Kim');
    }
}
[/code]
  • 서비스 클래스는 프레임워크나 DB 없이도 단독 테스트 가능

  • 진짜 유닛 테스트


⚠️ 주의사항

문제 구조 개선 아이디어
new 남발 DI로 변경
DB 직접 호출 리포지토리 인터페이스로 추상화
상태 유지 객체 setUp()에서 재사용
전역 상태 사용 피할 것 (ex. $_SESSION, $_POST)

🧠 요약

  • 유닛 테스트는 구조가 테스트를 허용하게 만들어야 가능

  • 컨트롤러, 프레임워크, DB, 외부 API에서 비즈니스 로직 분리

  • 의존성 주입 + 인터페이스 추상화로 모킹이 쉬운 구조 만들기

  • 테스트를 위한 설계 = 좋은 설계


댓글목록

등록된 댓글이 없습니다.