LogoSkills

Feature File Conventions (`.feature` 저작 규약)

**모든 텍스트(Feature명, Scenario명, 설명, Step, Background)는 영어로 작성**하고 `# 한글 번역` 주석을 덧붙입니다.

cc-e2e 가 생성/수정하는 모든 .feature 파일은 조직 표준을 따릅니다. 본 문서는 저작 관점의 체크리스트이며, 원전(原典)은 cc-flutter/skills/bdd-testing/SKILL.md 입니다.

1. 언어 규칙 (조직 표준)#

모든 텍스트(Feature명, Scenario명, 설명, Step, Background)는 영어로 작성하고 # 한글 번역 주석을 덧붙입니다.

이유:

  • step 파일명은 영어 snake_case 로 손으로 작성 (i_tap_the_login_button.dart, 코드 생성기 없음 — 네이밍은 사람이 이 규약을 따라 맞춘다)
  • step 함수명 영어 camelCase (iTapTheLoginButton) 와 Gherkin 문장이 1:1 매칭되어야 함
  • 조직 커밋 6003db6 refactor(testing): .feature 파일 언어 규칙 통일 에서 명시적 확정
@smoke
@auth
Feature: Login Page # 로그인 페이지
  Users can log in with email/password or social login. # 사용자가 이메일/비밀번호로 로그인하거나 소셜 로그인을 사용할 수 있습니다.

  Background:
    Given I am on the login page # 로그인 페이지에 있습니다

  Scenario: Successful login with valid email # 유효한 이메일로 로그인 성공
    When I enter { ' test@example.com ' } in the email field # 이메일 입력
    And I enter { ' password123 ' } in the password field # 비밀번호 입력
    And I tap the login button # 로그인 버튼 탭
    Then the store screen is displayed # 스토어 화면 표시

  Scenario: Form validation shows errors on empty submit # 빈 폼 제출 시 유효성 에러
    When I tap the login button # 로그인 버튼 탭
    Then the validation error should be displayed # 유효성 에러 표시

  Scenario: Background resume preserves session # 백그라운드 복귀 시 세션 유지
    When I press the home button # 홈 버튼
    And I return to the app # 앱 복귀
    Then the store screen is displayed # 스토어 표시

1.1 Business Value 문구 (권장)#

Feature 설명에 관점(역할)과 목적을 밝히면 시나리오의 "왜"가 분명해집니다. 필수는 아니지만, 여정(@journey)이나 기획/QA가 함께 검토하는 Feature에는 권장합니다:

Feature: Login Page # 로그인 페이지
  As a returning user # 재방문 사용자로서
  I want to log in with my email and password # 이메일과 비밀번호로 로그인하고 싶다
  So that I can access my personalized store # 그래야 개인화된 스토어에 접근할 수 있다

2. 실행 대상 (조직 표준)#

.feature 시나리오는 Patrol E2E 테스트로만 검증합니다 — 사람이 손으로 app/{app}/integration_test/scenarios/ 아래에 작성합니다(코드 생성기 없음, bdd_test_gen/co_test_gen/bdd_widget_test 어느 것도 이 파이프라인에 없습니다). 파일 배분은 §5.1 을 따릅니다 — 같은 Background 를 공유하는 Scenario 3건 이상이면 배치 파일 {name}_{screen}_batch_test.dart 하나로, @isolated 이거나 2건 이하면 {name}_{scenario}_test.dart 로 갑니다. 위젯 테스트 생성 경로는 폐지되었습니다 — 위젯 vs Patrol 을 고르는 @both/@widget-only/@patrol-only 태그는 더 이상 쓰지 않습니다. Gherkin 과 무관한 일반 위젯 테스트(순수 testWidgets, golden, BLoC 위젯 테스트)가 필요하면 BDD 와 별개로 cc-flutter/skills/widget-testing 규약을 따라 직접 작성합니다.

자세한 배경은 cc-flutter/skills/bdd-testing/SKILL.md, cc-flutter/skills/patrol-bdd-conventions/SKILL.md 참조.

3. 분류 태그#

태그의미
@smoke매 PR 스모크 스위트
@regression야간 회귀
@journey다단계 사용자 여정
@slow실행 시간 긴 시나리오
@isolated 화면 배치에서 제외 — 자기 patrolTest 로 단독 실행 (§5.1)
@{domain} @auth, @store, @reader 등 도메인

Feature 상단에 도메인 태그 + 시나리오별로 @smoke 등 등급·스케줄 태그를 조합.

@isolated 는 등급·스케줄이 아니라 실행 구조를 가르는 태그입니다. 붙이지 않은 것이 기본값(= 배치 대상)이며, 필수 조건은 §5.1 에 있습니다. @journey 는 정의상 화면을 넘나들므로 @journey 가 붙으면 @isolated 도 함께 붙습니다.

4. Step 문장 규약#

형식#

  • 영어 + # 한글 번역
  • 동사 현재형: I tap, I enter, should be displayed
  • 파라미터: {'value'} 문법 (step 함수의 추가 인자로 추출됨)

Given / When / Then 사용#

  • Given — 초기 상태 / 전제 (mock 주입, 로그인 상태 등)
  • When — 사용자 조작 (탭, 입력, 스크롤)
  • Then — 기대 결과 (요소 표시, 네비게이션, 에러 메시지)
  • And / But — 이전 키워드 이어받기

step 파일명 매핑#

  • Gherkin: I tap the login button
  • 파일: i_tap_the_login_button.dart
  • 함수: iTapTheLoginButton

자세한 규약은 cc-flutter/rules/bdd-test-patterns.md 의 "Step File Naming Rules" 참조.

Data Table (Given 필드 2개 이상)#

하나의 Given에 넣을 필드가 2개 이상이면 인라인 나열 대신 데이터 테이블(세로 key-value)을 씁니다:

# ❌ Given에 필드를 길게 나열
Given a user wants to add a bookmark with URL  " https://example.com " , title  " Example " , and description  " ... " 

 # ✅ 데이터 테이블로 정리
Given a user wants to add a bookmark with the following details: # 다음 정보로 북마크를 추가하려 함
  | URL         | https://example.com |
  | Title       | Example Website     |
  | Description | A helpful example   |

5. 단일 책임 원칙#

1 Scenario = 1 행위 검증:

  • ❌ "Login and edit profile and logout" — 하나의 시나리오에 3개 행위
  • ✅ Scenario 3개로 분리

예외: 의도적 여정 검증 (@journey 태그) — cross-feature 스토리 확인 시에만.

폼 필드 입력은 단일 고수준 When + 도메인 Step으로 — 필드를 하나씩 채우는 When/And를 나열하지 말고, 값은 위 Data Table로 Given에 두고 When은 하나의 고수준 동작(도메인 step)으로 통합합니다:

# ❌ 필드마다 개별 When/And
When I enter { ' https://example.com ' } in the URL field
And I enter { ' Example Website ' } in the title field
And I enter { ' A helpful example ' } in the description field
And I tap the save button

# ✅ Given data table + 단일 When
Given a user wants to add a bookmark with the following details: # 다음 정보로 북마크를 추가하려 함
  | URL         | https://example.com |
  | Title       | Example Website     |
  | Description | A helpful example   |
When they add the bookmark # 북마크를 추가함

허용 기준은 cc-flutter/skills/bdd-canonical-steps/SKILL.md 의 "도메인 특화 Step 허용 기준" — "복합 동작(3단계 이상)" 항목을 참조하세요. 단순 UI 이동 트레이스(화면 전환용 탭 나열)는 이 원칙의 대상이 아니며, 여러 값을 입력해 하나의 엔티티를 제출/저장하는 폼 시나리오에 적용합니다.

🔁 화면 배치는 이 원칙을 완화하지 않습니다. 배치가 합치는 것은 실행 세션이지 검증 단위가 아닙니다. "어차피 한 세션에서 도니까 한 Scenario 에 다 넣자" 는 정확히 반대 방향의 오독입니다 — 배치 덕분에 "쪼개면 도달 비용이 배로 든다"는 압력이 사라지므로, 이 원칙은 오히려 더 지키기 쉬워집니다. 잘게 쪼갠 Scenario 를 §5.1 대로 한 .feature 에 모으세요.

5.1 화면 단위 배치 — .feature 를 도달 상태로 나눈다#

Patrol E2E 는 가장 비싼 테스트이고, 비용의 대부분은 도달(앱 기동 → 로그인 → 그 화면까지 내비게이션)입니다. 그래서 같은 도달 상태를 공유하는 시나리오는 한 번 도달해 연달아 돌립니다.

배치 단위를 Gherkin 이 이미 표현하고 있는데, 그게 Background 입니다:

배치 단위 = 하나의 .feature 안에서 같은 Background 를 공유하는 Scenario 집합.

저작 규칙#

  1. .feature 는 "도달 상태(대개 화면)" 단위로 나눕니다. 서로 다른 Background 가 필요한 시나리오를 한 파일에 섞지 마세요 — 섞이는 순간 그 파일은 배치가 불가능해집니다. 한 기능에 화면이 여럿이면 store_home.feature · store_detail.feature 처럼 나눕니다.
  2. Background 에는 도달까지만 적습니다. 검증은 Background 가 아니라 각 Scenario 에 둡니다.
  3. 배치가 기본값입니다. 배치에 들어갈 수 없는 시나리오에만 @isolated 를 답니다.
  4. 하한선 3건 — 같은 Background 를 공유하는 Scenario 가 3건 이상일 때만 배치 파일로 묶습니다. 2건 이하는 기존 1파일 1시나리오 그대로입니다.

⚠️ Background 의 실행 의미가 표준 Gherkin 과 다릅니다#

표준 Gherkin 은 BackgroundScenario 마다 실행합니다. 이 조직의 배치 규약은 배치당 1회 실행하고, Scenario 사이에는 화면 리셋 step 을 돌립니다. .feature 를 읽는 사람이 표준 의미론을 가정하지 않도록 하는 의도적 이탈입니다.

@isolated 필수 조건#

하나라도 해당하면 배치에 넣지 않습니다:

  1. 인증 상태를 바꾼다 (로그아웃·계정 전환)
  2. 되돌릴 수 없는 서버 상태를 만든다 (결제·구매·삭제)
  3. 화면을 떠나 돌아오지 않는다 — @journey 는 정의상 해당
  4. 앱 재시작·프로세스 수준 상태가 검증 대상 (백그라운드 복귀·딥링크 콜드스타트·최초 권한 요청)
  5. 화면 리셋 계약(닫는다 · 되돌린다 · 단정한다)을 만족하는 리셋을 쓸 수 없다
@store
Feature: Store Home # 스토어 홈
  As a signed-in reader # 로그인한 독자로서
  I want to browse and filter the store # 스토어를 둘러보고 걸러내고 싶다
  So that I can find a book to read # 그래야 읽을 책을 찾을 수 있다

  # 이 Background 는 배치당 1회만 실행됩니다 (조직 규약, 표준 Gherkin 과 다름)
  Background:
    Given I am signed in # 로그인되어 있다
    And I am on the store home # 스토어 홈에 있다

  @smoke
  Scenario: Search returns matching books # 검색 결과 표시
    When I enter { ' flutter ' } in the search field # 검색어 입력
    Then the search result list should be displayed # 검색 결과 목록 표시

  Scenario: Category filter narrows the list # 카테고리 필터 적용
    When I tap the category chip # 카테고리 칩 탭
    Then the filtered list should be displayed # 걸러진 목록 표시

  Scenario: Empty search shows the empty state # 결과 없음 상태
    When I enter { ' zzzz ' } in the search field # 없는 검색어 입력
    Then the empty state should be displayed # 빈 상태 표시

  @isolated @journey
  Scenario: Purchasing a book moves it to my library # 구매 후 내 서재로 이동
    # 되돌릴 수 없는 서버 상태 + 화면 이탈 → 배치 불가
    When I purchase the first book # 첫 번째 도서 구매
    Then the my library screen is displayed # 내 서재 화면 표시

Patrol 쪽 대응(배치 파일 구조 · 리셋 계약 3항 · 실패/BLOCKED 의미론)의 정본은 cc-flutter/skills/patrol-bdd-conventions/SKILL.md 의 "화면 단위 배치" 절입니다.

6. Scenario Outline#

데이터 변형 자체가 검증 대상일 때만 사용합니다. 그 외에는 일반 Scenario가 기본값입니다 — 같은 로직을 값만 바꿔 반복할 필요가 없다면 Outline은 과잉입니다.

입력만 다른 반복 시 Scenario Outline + Examples:

Scenario Outline: Search returns results # 검색 결과 표시
  When I enter { ' < query > ' } in the search field # 검색어 입력
  Then  < count >   results should be displayed #  < count > 개 결과 표시

  Examples:
    | query  | count |
    | flutter | 15   |
    | patrol  | 3    |
    | xyz     | 0    |

7. cc-e2e 특수 규칙#

초안 단계 경고 주석#

scenario-draft 가 생성한 .feature 는 상단에 경고 주석 포함:

# DRAFT — generated from flutter-skill exploration journal
# journal: .claude/e2e-journal/sign_in.jsonl
# generated: 2026-04-23T12:34:56Z
# ⚠️ Review and refine before /cc-e2e:lock.

scenario-lock 시 이 주석이 제거되어야 lock 허용.

파일 위치#

위치용도
app/{app}/integration_test/features/{name}.feature 유일한 위치 — single-feature 든 cross-feature 든 여기 하나뿐. 문서(빌드 대상 아님). 파일은 §5.1 대로 도달 상태(대개 화면) 단위 로 나눈다
app/{app}/integration_test/scenarios/{name}_{screen}_batch_test.dart 대응하는 Patrol 테스트 — 같은 Background 를 공유하는 Scenario 3건 이상 (기본)
app/{app}/integration_test/scenarios/{name}_{scenario}_test.dart 대응하는 Patrol 테스트 — @isolated 이거나 2건 이하

feature 패키지(feature/{type}/{name}/) 안에는 .featuretest/src/bdd/ 도 존재하지 않습니다. scenario-draft 는 처음부터 위 위치에 생성하므로, 나중에 cross-feature 로 판명돼도 옮길 필요가 없습니다 — single-feature/cross-feature 구분이 목적지를 가르지 않습니다.

8. 금지 사항#

  • ❌ Korean-only Gherkin (영어 + 한글 주석 조합 필수)
  • ❌ semantic ref (button:Login) 를 Gherkin 본문에 포함
  • K.xxxKey 같은 코드 식별자를 Gherkin 에 포함
  • ❌ 1 Scenario 에 When 이 5개 이상 (책임 과다)
  • ❌ 폼 필드 3개 이상을 개별 When/And 로 나열 (Given data table + 단일 When 사용)
  • TODO 주석 잔존 상태 lock
  • ❌ 초안 경고 주석 잔존 상태 lock
  • ❌ 서로 다른 Background 가 필요한 Scenario 를 한 .feature 에 섞기 (배치가 불가능해짐 — §5.1)
  • ❌ §5.1 의 @isolated 조건에 해당하는 시나리오를 배치에 넣기 (로그아웃·결제·@journey·콜드스타트)
  • ❌ 배치를 핑계로 한 Scenario 에 여러 행위를 몰아넣기 (§5 는 그대로 유효)

참고#

  • 원전: cc-flutter/skills/bdd-testing/SKILL.md
  • Step 규약: cc-flutter/rules/bdd-test-patterns.md
  • 공유 step 사전: cc-flutter/agents/bdd-step-reuse-agent.md 가 관리