0Pricing
PHP Academy · 강의

테스트 주도 개발 워크플로

레드-그린-리팩터링 주기로 설계를 이끌어 갑니다.

테스트 주도 개발 워크플로은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

테스트 주도 개발은 설계 도구입니다

테스트 주도 개발은 흔히 테스트 기법으로 소개되지만, 진정한 가치는 설계를 압박하는 데 있습니다. 테스트를 먼저 작성하면 구현을 작성하기 전에 객체의 공개 인터페이스, 의존성, 계약을 정의할 수밖에 없습니다. 테스트는 부산물이고, 더 나은 설계가 결과물입니다.

레드, 그린, 리팩터링

이 주기는 세 가지 엄격한 단계로 이루어집니다.

  • 레드 — 다음의 아주 작은 동작을 검증하는 실패하는 테스트를 작성합니다. 올바른 이유로 실패해야 합니다.
  • 그린 — 코드가 보기 좋지 않더라도 통과하는 데 필요한 최소한의 코드만 작성합니다.
  • 리팩터링 — 통과 상태를 유지하면서 구현과 테스트를 정리합니다.

규율이 중요합니다. 레드를 건너뛰지 마십시오. 아무것도 테스트하지 않을 수 있습니다. 또한 그린 단계에서 과도하게 구현하지 마십시오.

레드: 실패하는 테스트를 먼저 작성하기

백분율 할인을 적용하는 PriceCalculator를 만들어 보겠습니다. 아직 존재하지 않는 동작을 검증하는 테스트부터 시작하십시오. 실행하면 클래스가 정의되지 않았기 때문에 실패해야 하며, 이것은 유효한 레드 단계입니다.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    public function test_applies_a_percentage_discount(): void
    {
        $calc = new PriceCalculator();
        // 100 with 10% off -> 90.0
        self::assertSame(90.0, $calc->withDiscount(100.0, 10));
    }
}

그린: 통과에 필요한 최소 코드

이제 상태를 그린으로 바꾸는 가장 작은 구현을 작성하십시오. 아직 이를 요구하는 실패하는 테스트가 없으므로 반올림 규칙, 유효성 검사 또는 통화 처리를 추가하고 싶은 마음을 억누르십시오.

<?php
final class PriceCalculator
{
    public function withDiscount(float $amount, float $percent): float
    {
        return $amount - ($amount * $percent / 100);
    }
}

$calc = new PriceCalculator();
var_dump($calc->withDiscount(100.0, 10)); // float(90)

일반성을 이끌어 내는 삼각 측량

테스트 하나는 값을 하드코딩하는 것으로 통과시킬 수 있습니다. 삼각 측량은 서로 다른 두 번째 예를 추가하여 구현이 일반화되도록 강제합니다. 단순한 해결책으로는 흉내 낼 수 없는 사례를 추가하여 실제 수식을 구현하도록 만드십시오.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    public function test_zero_percent_is_unchanged(): void
    {
        self::assertSame(50.0, (new PriceCalculator())->withDiscount(50.0, 0));
    }

    public function test_full_discount_is_free(): void
    {
        self::assertSame(0.0, (new PriceCalculator())->withDiscount(50.0, 100));
    }
}

경계 사례를 사양으로 이끌어 내기

테스트 주도 개발에서는 새로운 요구 사항이 새로운 실패하는 테스트가 됩니다. 음수 할인은 유효하지 않다고 가정해 보겠습니다. 먼저 예외가 발생할 것으로 예상하는 테스트를 작성하십시오. 아직 아무것도 예외를 발생시키지 않으므로 테스트는 실패합니다. 이 테스트가 계약을 문서화합니다.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorValidationTest extends TestCase
{
    public function test_rejects_negative_discount(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        (new PriceCalculator())->withDiscount(100.0, -5);
    }
}

다시 그린으로, 그런 다음 리팩터링

새 테스트를 통과하도록 방어 코드를 추가한 다음, 자신 있게 리팩터링하십시오. 기존 테스트가 회귀를 잡아냅니다. 반올림도 테스트나 알려진 요구 사항이 정당화한 뒤에야 한 번 도입했다는 점에 주목하십시오.

<?php
final class PriceCalculator
{
    public function withDiscount(float $amount, float $percent): float
    {
        if ($percent < 0 || $percent > 100) {
            throw new \InvalidArgumentException('percent must be 0..100');
        }
        return round($amount - ($amount * $percent / 100), 2);
    }
}

var_dump((new PriceCalculator())->withDiscount(19.99, 15)); // float(16.99)

데이터 제공자로 예를 압축하기

동작이 안정되면 여러 예시 쌍을 데이터 제공자를 사용하는 하나의 매개변수화된 테스트로 통합하십시오. 이렇게 하면 레드/그린 주기를 빠르게 유지하면서 의도를 표에서 읽을 수 있습니다.

<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;

final class DiscountTableTest extends TestCase
{
    #[DataProvider('cases')]
    public function test_discounts(float $amount, float $pct, float $expected): void
    {
        self::assertSame($expected, (new PriceCalculator())->withDiscount($amount, $pct));
    }

    public static function cases(): array
    {
        return [
            'no discount'   => [100.0, 0, 100.0],
            'ten percent'   => [100.0, 10, 90.0],
            'free'          => [100.0, 100, 0.0],
        ];
    }
}

주기를 빠르게 유지하기

테스트 주도 개발은 피드백 주기가 몇 분이 아니라 몇 초일 때만 효과가 있습니다. 실용적인 방법은 다음과 같습니다.

  • 단일 파일을 실행하거나 필터를 사용하십시오: phpunit --filter test_full_discount_is_free.
  • --testdox를 사용하여 테스트를 동작 사양처럼 읽으십시오.
  • 단위 테스트에서 입출력을 제거하십시오. 내부 주기에서는 데이터베이스, 네트워크 또는 파일 시스템을 사용하지 마십시오.
vendor/bin/phpunit --testdox --filter PriceCalculator

테스트도 리팩터링하기

리팩터링 단계의 대상은 운영 코드뿐 아니라 테스트 모음이기도 합니다. 도우미와 setUp()으로 중복을 제거하고, 동작을 기준으로 테스트 이름을 지정하며, 더 이상 의미 있는 내용을 단언하지 않는 테스트는 삭제하십시오. 테스트는 영구적으로 유지 관리해야 하는 코드이므로 같은 주의를 기울이십시오.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    private PriceCalculator $calc;

    protected function setUp(): void
    {
        $this->calc = new PriceCalculator(); // shared setup, no duplication
    }

    public function test_ten_percent(): void
    {
        self::assertSame(90.0, $this->calc->withDiscount(100.0, 10));
    }
}

테스트 주도 개발이 의존성을 형성합니다

테스트를 먼저 작성하기 때문에 다루기 불편한 의존성이 즉시 드러납니다. 테스트에서 클래스를 생성하기 어렵다면 설계에 대한 피드백입니다. 클래스 내부에서 new로 협력 객체를 만들지 말고 주입하십시오. 아래의 할인 계산기는 반올림 정책을 하드코딩하지 않고 전달받도록 하여 테스트할 수 있습니다. 테스트 주도 개발이 이러한 연결 지점을 만들어 낸 것입니다.

<?php
interface RoundingPolicy { public function round(float $v): float; }

final class PriceCalculator
{
    public function __construct(private RoundingPolicy $rounding) {}

    public function withDiscount(float $amount, float $percent): float
    {
        return $this->rounding->round($amount - ($amount * $percent / 100));
    }
}
// Tests inject a deterministic RoundingPolicy; no hidden global behavior.

빠른 확인

레드 단계의 목적은 무엇입니까?

정리

테스트 주도 개발을 설계 규율로 실습했습니다.

  • 레드-그린-리팩터링: 실패하는 테스트, 최소한의 코드, 그리고 그린 상태에서의 정리입니다.
  • 삼각 측량은 일반성을 강제하고, 새로운 요구 사항은 새로운 실패하는 테스트로 나타납니다.
  • 데이터 제공자는 안정된 사례를 압축하며, --filter/--testdox는 주기를 빠르게 유지합니다.
  • 테스트도 리팩터링하십시오. 테스트 역시 장기간 유지되는 코드입니다.

다음 주제는 유연한 테스트 대역과 모의 객체 라이브러리입니다.

자주 묻는 질문

“테스트 주도 개발 워크플로” 강의는 무료인가요?

네 — “테스트 주도 개발 워크플로” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“테스트 주도 개발 워크플로”에서 뭘 배우나요?

레드-그린-리팩터링 주기로 설계를 이끌어 갑니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

PHP Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“테스트 주도 개발 워크플로” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 테스트 주도 개발 워크플로
  2. Mockery로 모킹과 스터빙
  3. 통합 테스트와 기능 테스트
  4. Infection으로 변이 테스트
← PHP Academy(으)로 돌아가기