본문으로 건너뛰기
AI 시대의 TDD
AI 시대의 TDD: 모델이 먼저 빨간불에 닿게 하세요 的文章封面图

AI 시대의 TDD: 모델이 먼저 빨간불에 닿게 하세요

AI 보조

AI 프로그래밍에 TDD가 필요한 이유는 무엇입니까? 요점은 공식적으로 "테스트를 먼저 작성"하는 것이 아니라 구현이 빨간색에서 녹색으로 변경되기 전에 검증 가능한 실패를 생성하는 것입니다.

먼저 결론부터 얘기해보자

옆에 청록색 고글을 착용한 황금 작성자 아바타 캐릭터와 함께 빨간색 테스트 조명으로 차단된 손으로 그린 AI 코드 기계
AI는 코드를 신속하게 전달할 수 있지만 TDD에서는 먼저 검증 가능한 빨간불이 켜져야 합니다.

AI가 코드를 작성한 후 TDD는 구식이 아니지만 위치가 변경되었습니다.

과거에는 TDD에 대해 이야기할 때 프로그래머의 자기 훈련에 대해 자주 이야기했습니다. 먼저 테스트를 작성한 다음 구현을 작성하고 작은 단계로 리팩토링하는 것입니다. AI 프로그래밍의 경우 브레이크 시스템과 비슷합니다. 모델이 가장 잘하는 것이 가장 위험한 것이기도 하기 때문입니다. 모델은 완전해 보이는 큰 코드 조각을 빠르게 작성할 수 있습니다.

기능 구현을 요청하면 다음을 제공할 수 있습니다.

  • 구현 파일
  • 일련의 테스트
  • 설명
  • "완료"라는 단어

문제는 "완전해 보이는 것"이 엔지니어링 측면에서는 수행되지 않는다는 것입니다. 공학적 의미에서 완료하려면 최소한 다음과 같이 답하십시오.

이 동작은 명시적으로 실패한 테스트로 정의됩니까? 구현으로 인해 이 실패가 녹색으로 바뀌었나요? 녹색으로 변한 후 동작 변경 없이 코드를 정리했나요?

AI 시대에 TDD가 다시 거론되는 이유다.

프로세스를 발전된 것처럼 보이게 만드는 것이 아니라 '모델에 대한 신뢰'를 '피드백에 대한 신뢰'로 바꾸는 것입니다.

1. 일반적인 오해: TDD는 "먼저 테스트 작성"이 아닙니다.

손으로 그린 테스트 스트립이 빨간색 오류 신호로 바뀌고 작성자 아바타 캐릭터가 빨간색 표시등을 가리킵니다.
TDD의 핵심은 테스트 파일이 일찍 나타나는 것이 아니라 실패 피드백이 충분히 일찍 나타나는 것입니다.

많은 사람들이 TDD를 의식으로 이해하기 때문에 싫어합니다.

先写测试。
再写代码。
最后跑一下。

이것은 확실히 지루하고 쉽게 형식주의로 바뀔 수 있습니다.

정말 유용한 TDD는 "테스트 파일이 일찍 나타나는 것"이 아니라 "실패가 충분히 일찍 나타나는 것"입니다.

핵심은 테스트용이 아닌 빨간색

TDD의 첫 번째 단계는 TEST가 아닌 RED라고 합니다.

빨간색은 시스템이 확실히 실패하도록 먼저 테스트를 작성한다는 의미입니다. 이것이 실패하려면 세 가지가 충족되어야 합니다.

  1. 실패합니다.
  2. 대상 동작이 존재하지 않기 때문에 실패합니다.
  3. 예상대로 실패합니다.

빨간색이 먼저 보이지 않으면 뒤따르는 녹색은 의미가 없습니다.

예를 들어 slugify("Hello World") -> "hello-world"을 구현하려고 합니다. 귀중한 RED는 "내가 테스트 파일을 작성했습니다"가 아니라 다음과 같습니다.

Test: tests/test_slugify.py
Command: pytest tests/test_slugify.py -q
Failure: NameError: name 'slugify' is not defined
Reason: 目标函数还不存在,符合预期

테스트가 사양이 되는 시기입니다. 다음 단계를 달성하려면 이 동작을 true로 만들기만 하면 됩니다.

먼저 친환경을 선택한 다음 테스트를 구성하세요. 일반적으로 스토리를 구성합니다.

AI가 다른 방향으로 가는 것은 쉽습니다. 먼저 구현을 작성하고 나중에 테스트를 추가합니다.

이것은 원활한 경험입니다. 코드가 실행되고 테스트가 완료되는 것을 보면 마음속에 "거의"라는 느낌이 들 것입니다. 그러나 여기에는 치명적인 문제가 있습니다. 테스트는 현재 구현을 소급하여 구현하는 것일 뿐일 가능성이 높습니다.

"요구 사항이 무엇이어야 하는지"를 묻는 것이 아니라 "쉽게 통과할 수 있도록 현재 코드를 작성하는 방법"을 묻는 것입니다.

이것이 바로 AI 글쓰기 테스트에서 종종 다음과 같은 냄새가 나는 이유입니다.

  • 주장이 현재 구현에 너무 구체적입니다.
  • 모의가 너무 많아 실제 경계가 측정되지 않습니다.
  • 행복한 경로만 테스트
  • 기존 코드를 통과시키려면 어설션을 매우 넓게 만드십시오.
  • 어떤 테스트도 이전 코드가 원래 틀렸다는 것을 증명할 수 없습니다.

TDD는 그 반대를 요구합니다. 먼저 요구 사항이 실패하도록 하고 코드가 요구 사항을 따라잡도록 합니다.

2. AI 시대에 TDD가 더 필요한 이유

AI 프로그래밍의 핵심 모순은 '코드가 느리게 작성된다'가 아니라 '피드백이 늦게 온다'는 것이다.

TDD가 없으면 일반적으로 다음과 같이 작업합니다.

描述需求 -> AI 写一堆代码 -> 人肉看 diff -> 跑一下 -> 发现问题 -> 回头修

질문은 끝까지 쌓이게 됩니다. 그것이 틀렸다는 것을 알게 될 때쯤에는 세 가지 범주가 함께 섞여 있을 수 있습니다.

  • 요구사항에 대한 오해
  • 구현 경로가 잘못되었습니다.
  • 리팩토링으로 인해 기존 동작이 중단됨

TDD의 역할은 이 긴 사슬을 줄이는 것입니다.

모델에 결정 가능한 목표를 제공합니다.

"우아하게 쓰기"는 목표가 아닙니다.

"사용자의 로그인 상태가 만료된 후 자동으로 로그인 페이지로 돌아가는 것"은 충분히 구체적이지 않습니다.

더 나은 목표는 다음과 같습니다.

当 access token 过期时:
1. 请求返回 401。
2. 客户端清理本地 session。
3. 用户被重定向到 /login。
4. 原始目标地址被保存在 redirect 参数里。

한 단계 더 나아가 그 중 하나를 실패한 테스트로 바꾸십시오.

given expired session
when user opens /settings
then app redirects to /login?redirect=/settings

이때 AI는 더 이상 "로그인 상태 만료를 처리하는 방법"을 추측하지 않고 명확한 동작을 완료합니다.

대규모 작업을 작은 닫힌 루프로 나눕니다.

AI가 통제력을 잃기 가장 쉬운 곳은 모든 것을 한 번에 수행하는 것입니다.

로그인, 권한, 새로 고침 토큰, 오류 프롬프트 및 경로 점프를 한 번에 구현하면 결국 큰 차이가 발생합니다. 작동할 수도 있지만 검토 비용이 높습니다. 비즈니스, 상태, 라우팅, 경계, 테스트 및 리팩토링을 동시에 판단해야 합니다.

TDD의 리듬은 다음과 같습니다.

一个行为 -> 一个失败测试 -> 最小实现 -> 变绿 -> 再下一个行为

한 번에 소량씩만 진행하세요. 이해할 수 있을 만큼 작고, AI가 이야기를 만들어내기 어려울 만큼 작으며, 실패했을 때 빠르게 찾아낼 수 있을 만큼 작습니다.

모델이 "부드럽게 플레이"하도록 제한합니다.

AI의 일반적인 문제는 지나친 열성입니다.

경계 버그 수정을 요청하면 도우미가 추출됩니다. 테스트를 추가하도록 요청하면 구현이 변경됩니다. 리팩토링을 요청하면 동작이 변경됩니다.

TDD는 단계를 사용하여 이러한 작업을 구분합니다.

스테이지해야 할 일하지 말아야 할 것
레드실패한 테스트 작성프로덕션 구현 작성
그린최소 구현 작성녹색이 되도록 테스트 수정
리팩터구조 정리새로운 행동 도입

이 표는 "조심하세요"보다 더 유용합니다. 이는 모델이 어느 단계에 있는지 알려주고 인간이 경계 위반을 더 쉽게 감지할 수 있도록 해줍니다.

3. 레드와 그린의 재구성: 세 개의 문이 아닌 세 개의 슬로건

손으로 그린 3개의 문: 빨간불, 녹색, 재건축, 체크보드를 들고 있는 작가의 아바타 캐릭터
적록 리팩토링은 세 개의 관문과 비슷합니다. 먼저 격차를 증명하고, 그 다음 현재 동작을 통과시키고, 마지막으로 구조를 정리합니다.

“Red, Green, Refactor”는 쉽게 슬로건이 될 수 있습니다. 실제 사용에서는 세 개의 문처럼 보여야 합니다. 문을 통과할 때마다 증거를 남겨야 합니다.

첫 번째 문: RED, 요구 사항이 충족되지 않았음을 증명

RED 단계에서 가장 중요한 질문은 다음과 같습니다.

이 테스트가 실패하면 아직 목표 행동이 부족하다는 것을 증명하는 것인가요?

나쁜 빨간색:

assert True

그다지 좋은 RED도 아닙니다.

assert "hello" in format_title("Hello World")

너무 넓습니다. 많은 잘못된 구현도 통과됩니다.

더 나은 빨간색:

def test_slugify_lowercases_and_uses_hyphen():
    assert slugify("Hello World") == "hello-world"

이 테스트는 작지만 명확합니다. 입력, 출력 및 동작을 지정합니다.

두 번째 문: 녹색, 현재 테스트만 통과하도록 놔두세요.

GREEN 단계는 최종 아키텍처를 작성하는 단계가 아닙니다.

임무는 단 하나입니다. 최소한의 코드로 현재 실패한 테스트를 통과하는 것입니다.

이 진술은 직관에 반하는 것처럼 들립니다. 많은 사람들은 "최소 구현"이 너무 추악한 것은 아닌지 걱정합니다. 네, 때로는 추악할 수도 있습니다. 그러나 그 가치는 설계 압력을 유지하는 데 있습니다.

첫 번째 테스트가 다음과 같은 경우:

def test_slugify_lowercases_and_uses_hyphen():
    assert slugify("Hello World") == "hello-world"

허용되는 GREEN은 다음과 같습니다.

def slugify(text: str) -> str:
    return text.lower().replace(" ", "-")

중국어, 악센트, 연속 구두점, 이모티콘, SEO 특수 사례를 바로 지원할 필요는 없습니다. 이는 이후 테스트를 통해 구동되어야 합니다.

세 번째 문: REFACTOR, 구조만 변경하고 동작은 변경하지 않습니다.

REFACTOR 단계는 AI가 가장 혼동하기 쉬운 단계입니다.

"코드 정리"는 "그런데 코드를 향상시키다"로 해석됩니다. 이것은 작동하지 않습니다. 리팩토링의 정의는 매우 좁습니다. 외부 동작은 변경되지 않지만 내부 구조는 더 좋아집니다.

좋은 리팩토링은 다음과 같습니다:

  • 변수 이름을 좀 더 정확한 이름으로 변경하세요.
  • 반복되는 표현 추출
  • 너무 깊은 조건 분기 제거
  • 모듈 책임을 더 명확하게 하기 위해 기능 위치 이동

잘못된 리팩토링은 다음과 같습니다.

  • 이제 새로운 입력이 지원됩니다.
  • 오류 메시지가 쉽게 변경되었습니다.
  • 종속성을 쉽게 변경했습니다.
  • 테스트 어설션을 편리하게 변경했습니다.

판단 기준은 간단합니다.

이 커밋이 방금 refactor:이라고 불렸다면 테스트 전후에 녹색이 동일해야 하며 사용자 동작도 동일해야 합니다.

4. 좋은 테스트의 맛

TDD는 더 많은 테스트가 더 좋아지는 것에 관한 것이 아닙니다. AI는 가치가 거의 없는 일련의 테스트를 생성하는 데에도 매우 능숙합니다.

더 중요한 것은 맛을 테스트하는 것입니다.

좋은 테스트는 스펙과 같습니다

좋은 테스트는 비즈니스 사양처럼 읽어야 합니다.

当用户没有权限时,保存按钮不可点击。
当标题为空时,表单显示错误信息。
当重复提交同一个请求时,只创建一条记录。

내부적으로 수행되는 작업이 아니라 외부 작업과 관련이 있습니다.

잘못된 테스트는 구현 노트처럼 보입니다.

应该调用 validateInput 三次。
应该读取 state.user.flags。
应该触发 handleClick 内部函数。

구현 세부 사항이 묶여지면 리팩토링이 어려워집니다. 방금 내부 구조를 변경했지만 테스트가 대규모로 실패했습니다. 이러한 테스트는 코드를 보호하기보다는 코드를 동결시킵니다.

좋은 테스트에는 경계가 있습니다

테스트는 단 하나의 질문에만 답하도록 설계되는 것이 가장 좋습니다.

테스트가 다음을 주장하는 경우:

  • 올바른 형식
  • 권한이 올바른지
  • 네트워크 요청이 올바른지
  • 토스트 카피가 정확합니다
  • 데이터베이스 상태가 올바른지

실패하면 문제가 무엇인지 알기가 어렵습니다.

AI는 특히 한 번에 많은 것을 증명하려고 하기 때문에 이러한 "대규모, 포괄적" 테스트를 작성하는 경향이 있습니다. TDD는 그 반대입니다. 즉, 작업, 실패, 구현입니다.

좋은 테스트는 구현의 부정 행위를 어렵게 만듭니다.

테스트가 너무 구체적인 입력만 다루는 경우 AI는 일치하는 가짜 구현을 작성할 수 있습니다.

예를 들면:

def slugify(text: str) -> str:
    return "hello-world"

첫 번째 테스트에서는 통과하지만 두 번째 테스트에서는 실제 논리가 강제로 실행됩니다.

def test_slugify_handles_another_title():
    assert slugify("Test Driven Development") == "test-driven-development"

따라서 TDD는 항상 하나의 테스트만 작성하는 것이 아니라 각 라운드마다 하나의 행동 압력만 추가합니다. 압력이 점차 증가하고 디자인이 점차 커집니다.

5. AI는 어떻게 TDD를 우회할 것인가?

AI가 TDD를 우회하는 세 가지 방법(테스트 변경, 과적합, 무작위 모의)을 손으로 그렸습니다. 작가의 아바타 캐릭터가 체크리스트를 들고 있습니다.
AI가 테스트를 변경하거나 구현을 덮어쓰거나 모의를 사용하여 경계를 가리려고 시도하는 경우 프로세스를 증거로 되돌려야 합니다.

AI는 당연히 테스트를 존중하지 않기 때문에 이 부분을 분명히 해야 합니다.

최적화 목표는 간단합니다. 방금 언급한 작업을 완료하는 것입니다. "테스트 통과"라고 말하면 인간이 원하지 않는 일부 작업을 수행할 수 있습니다.

첫 번째 유형: 테스트를 녹색으로 변경

가장 일반적인 것:

  • assert slugify("Hello World") == "hello-world"을 현재 출력으로 변경
  • 실패한 어설션 제거
  • 테스트에 skip 추가
  • 엄격한 주장을 느슨한 주장으로 변경

이것은 TDD가 아닙니다. 이것은 빨간불을 끄는 것입니다.

두 번째 유형: 과적합 구현 작성

예를 들어 테스트에는 입력이 하나만 있습니다.

def test_slugify_lowercases_and_uses_hyphen():
    assert slugify("Hello World") == "hello-world"

모델은 다음과 같이 작성될 수 있습니다.

def slugify(text: str) -> str:
    if text == "Hello World":
        return "hello-world"
    return text

지금은 꾸짖을 필요가 없습니다. 구현이 계속해서 하드 코딩될 수 없도록 다음 동작을 계속 추가해야 합니다.

세 번째 방법: 모의를 사용하여 실제 경계를 덮습니다.

AI는 모의를 좋아합니다. Mock은 테스트 작성을 더 쉽게 만들고 많은 실제 문제를 사라지게 만듭니다.

조롱할 수 없는 것은 아니지만 이렇게 질문해야 합니다.

지금 제가 조롱하고 있는 것은 느린 의존성인가요, 아니면 정말 검증하고 싶은 경계인가요?

결제 콜백 구문 분석을 검증하고 싶지만 구문 분석 레이어를 모의하는 경우 테스트는 의미가 없습니다.

6. TDD를 사용하지 말아야 할 경우

TDD에는 가치가 있지만 모든 것이 가치 있는 것은 아닙니다.

부적합한 장면

  • 순수한 시각적 미세 조정
  • 일회성 스크립트
  • 기술 탐색 데모
  • 요구사항 자체가 명확하게 고려되지 않은 프로토타입
  • 테스트 프레임워크가 아직 창고를 설정하지 않았습니다.

이러한 시나리오에서는 탐색 속도를 먼저 추구하고 프로세스에 방해를 받지 마십시오.

적합한 장면

  • 버그 수정
  • 권한, 청구, 상태 머신
  • 데이터 변환 및 경계 처리
  • 오랫동안 유지될 핵심 모듈
  • AI가 반복적으로 수정하는 코드 경로

판단 기준은 "이 기능이 훌륭한지 아닌지"가 아니라,

틀리면 비용이 뻔한가요?

비용은 명백하므로 먼저 테스트를 작성해 볼 가치가 있습니다.

7. 실행 가능한 멘탈 메소드

AI에게 단 한 문장만 준다면 다음과 같이 말하지 않을 것입니다.

请高质量实现这个功能。

나는 말할 것입니다 :

先写一个失败测试,运行它,确认失败原因符合预期。不要写实现,直到我说 go。

이 문장은 모델에게 "잘 행동하라"고 요구하는 것이 아니라, 확인할 수 있는 프로세스에 들어가라고 요구하기 때문에 품질이 더 높습니다.

좀 더 완벽함:

每轮只处理一个行为。
RED:写一个失败测试并运行。
GREEN:写最小实现,不改测试。
REFACTOR:只在绿色状态下整理结构。
每轮报告测试文件、命令、失败原因、通过结果。

이것이 AI시대 TDD의 핵심이다.

테스트나 프로세스에 대해 미신을 믿는 것이 아니라 모든 단계에 대한 증거를 확보하는 것에 대한 것입니다.

마무리

AI 프로그래밍에 가장 필요한 것은 더 많은 코드가 아니라 더 짧은 피드백입니다.

TDD의 가치는 다음과 같습니다. "나는 그것이 옳다고 생각했습니다"를 "여기에 실패가 있었고 그 다음에는 녹색으로 변했습니다."로 바뀌었습니다. 변화는 작지만 충분히 현실적입니다.

한 문장만 기억한다면 다음을 기억하세요.

AI가 코드를 직접 전달하도록 하지 마세요. 먼저 빨간색 빛을 전달한 다음 빨간색 빛을 녹색으로 바꾸도록 합니다.

다음 글 실용 가이드에서는 이 리듬을 직접 복사할 수 있는 워크플로로 바꿀 것입니다.

추천 리소스

TDD, AI agents and coding with Kent Beck
실용주의 엔지니어가 Kent Beck을 인터뷰합니다. AI 프로그래밍의 작은 단계 피드백, 테스트 보호 및 엔지니어링 규율에 중점을 둡니다.YouTube

Augmented Coding: Beyond the Vibes

Kent Beck's first-person account of using AI agents while keeping engineering discipline.

Kent BeckTidy First
이동

Red/green TDD

Why red/green TDD is a useful compact instruction for coding agents.

Simon Willisonsimonwillison.net
이동

Test-Driven Development: By Example

The original book that defined the red-green-refactor loop.

Kent BeckAmazon
이동

댓글

목차

AI 시대의 TDD: 모델이 먼저 빨간불에 닿게 하세요 | Yu의 사이버 데스크