TIL/Claude Code

[TIL][Playwright] E2E 검증에서 가장 위험한 코드는 "항상 통과하는 검증"이다

아람2 2026. 4. 12. 16:54
반응형

Bench에서 분자를 삭제하는 시나리오가 있다

Scenario: 분자를 삭제한다
    Given Bench 페이지에 진입한다
    When 첫 번째 분자를 삭제한다
    Then 분자가 삭제되었음을 확인한다

검증이 매번 PASS였다

그런데 Then Step 코드를 열어보니 이랬다

@then("분자가 삭제되었음을 확인한다")
def verify_molecule_removed(bench_page, bench_context):
    bench_page.take_screenshot("molecule_deleted")
    logger.info("✓ 분자 삭제 완료")

스크린샷 찍고, 로그에 체크마크 출력하고, pytest는 PASS를 반환한다

실제로 분자가 삭제되었는지는 아무도 확인하지 않는다

삭제 API가 500을 반환해도 PASS다

화면에 분자가 그대로 남아 있어도 PASS다

이 패턴의 이름: Silent Pass

logger.info + take_screenshot 조합은 사람 눈에 "검증"처럼 보인다

콘솔에 ✓ 분자 삭제 완료가 출력되고, 스크린샷 파일도 저장된다

그래서 코드 리뷰에서도 잘 안 걸린다

하지만 코드 레벨에서 assert가 한 줄도 없다

예외가 발생하지 않는 한 무조건 통과한다

같은 패턴이 Given에도 있었다

@given("모든 단백질이 비활성화된 상태이다")
def all_proteins_deactivated(protein_page):
    logger.info("✓ 단백질 비활성화 (프로젝트 초기 상태)")

"프로젝트 초기 상태"라는 가정 아래 상태 확인 없이 통과한다

전체 스위트를 순차 실행하면 이전 시나리오가 단백질을 활성화한 채로 남겨둘 수 있다

그래도 이 Given은 항상 통과하고, 이후 When에서 엉뚱한 상태를 기반으로 동작한다

해결: 변화량 기반 검증

삭제를 검증하려면 "삭제 전 상태"를 알아야 한다

@when("첫 번째 분자를 삭제한다")
def delete_first_molecule(bench_page, bench_context):
    # 삭제 전 분자 수 기록
    bench_context["molecule_count_before_delete"] = (
        bench_page.get_displayed_molecule_count()
    )
    bench_page.delete_first_molecule()

When에서 액션을 실행하기 전에 현재 상태를 bench_context에 저장한다

@then("분자가 삭제되었음을 확인한다")
def verify_molecule_removed(bench_page, bench_context):
    count_before = bench_context["molecule_count_before_delete"]
    count_after = bench_page.get_displayed_molecule_count()
    assert_with_screenshot(
        bench_page,
        count_after < count_before,
        f"분자 수 감소해야 함 (이전: {count_before}, 현재: {count_after})"
    )

Then에서 액션 후 상태를 조회하고, 변화량을 비교한다

삭제가 실패하면 count_after == count_before이고, assert가 실패하면서 스크린샷과 함께 정확한 메시지를 남긴다

Given 사전조건도 동일하게 적용

@given("모든 단백질이 비활성화된 상태이다")
def all_proteins_deactivated(protein_page):
    is_deactivated = protein_page.is_bench_protein_deactivated()
    given_precondition(
        protein_page,
        is_deactivated,
        "단백질이 비활성화 상태가 아닙니다",
        "all_proteins_deactivated"
    )

given_precondition은 조건이 불충족하면 pytest.fail로 즉시 중단한다

"사전조건이 맞지 않으면 검증을 시작하지 않는다"는 원칙이다

Silent Pass 감지 체크리스트

항목 위험 신호
Then Step에 `assert`가 없다 `logger.info`만으로 PASS 처리
Given Step에 상태 확인이 없다 "초기 상태"라고 가정만
`take_screenshot`만 호출한다 사람용 증거만 남기고 코드 검증 없음
예외 없이 항상 통과한다 어떤 상태에서든 PASS

자동화 검증에서 가장 위험한 코드는 "실패하는 검증"이 아니라 "항상 통과하는 검증"이다

실패하는 코드는 즉시 알 수 있다

하지만 항상 통과하는 코드는 "검증이 동작하고 있다"는 거짓 신뢰를 준다

이번에 review_checklist.sh에 "Then Step에 assert 없이 logger.info만 사용" 패턴 탐지를 추가했다

# Then Step에서 assert 없이 logger.info만 있는 함수 탐지
grep -n "def.*then\|def.*verify\|def.*confirm" "$file" | while read line; do
    # 해당 함수 범위 내에 assert가 없으면 경고
done

코드 작성 완료 시점에 자동으로 피드백된다

반응형