LogoSkills

bdd:refactor-steps

BDD Gherkin step 재사용성 분석 및 리팩토링

항목내용
Invoke/bdd:refactor-steps
Aliases/bdd:refactor, /bdd:step-audit
Categorytest-quality
Complexitymoderate

/bdd:refactor-steps#

기존 BDD .feature 파일의 step 표현을 분석하여 재사용률을 높이고, 중복 step 파일을 제거합니다.

Triggers#

  • BDD 테스트 유지보수 시
  • 새 feature 작성 전 기존 step 재사용 확인
  • step 중복이 의심될 때
  • /bdd:generate 실행 전 사전 점검

사용법#

/bdd:refactor-steps                          # 전체 프로젝트 분석
/bdd:refactor-steps store                    # 특정 feature만 분석
/bdd:refactor-steps --fix                    # 자동 수정 모드
/bdd:refactor-steps --report                 # 보고서만 출력
/bdd:refactor-steps --scope console          # 콘솔 feature만

Parameters#

ParameterRequiredDescriptionExample
feature_name No 특정 feature 분석 (생략 시 전체) store
--fixNo분석 + 자동 수정true
--reportNo분석 보고서만 출력true
--scope No feature 범위 제한 application, console, common
--dry-runNo변경 내용 미리보기 (수정 없음)true

실행 흐름#

Phase 1: Step 인벤토리 수집#

프로젝트 전체의 step 파일을 스캔하여 인벤토리를 구축합니다.

# 1. 모든 step 파일 수집
find . -path  " */test/src/bdd/step/*.dart "   -exec basename {} \;

# 2. 중복 파일명 집계
find . -path  " */test/src/bdd/step/*.dart "   -exec basename {} \; | sort | uniq -c | sort -rn

# 3. feature별 step 수 집계
for dir in feature/*/; do
  count=$(find  " $dir "   -path  " */test/src/bdd/step/*.dart "   | wc -l)
  echo  " $count $dir " 
 done | sort -rn

수집 항목:

  • step 파일명 (= step 표현)
  • step 함수 시그니처
  • 사용하는 feature 목록
  • 구현 상태 (UnimplementedError / no-op / 실구현)

Phase 2: 중복 및 유사 step 분석#

2.1 정확히 동일한 step (Exact Duplicates)

동일한 파일명이 2개 이상의 feature에 존재하는 경우:

the_error_message_should_be_displayed.dart  → 33개 feature
the_loading_indicator_should_be_displayed.dart → 27개 feature
the_search_results_should_be_displayed.dart → 16개 feature

2.2 유사한 step (Near Duplicates)

Canonical Step Dictionary 기준으로 정규화 가능한 변형:

현재 표현표준 표현변환 규칙
the X is loadedthe X has loadedtense 통일
the X is in loading statethe X is loading축약
an error message should be displayed the error message should be displayed 관사 통일
the book detail screen should be displayed the book detail page should be displayed screen→page
I click the X buttonI tap the X buttonclick→tap

2.3 파라미터화 가능한 step

구체적인 이름만 다르고 구조가 동일한 step:

# 현재: 각각 별도 step
i_tap_the_search_button.dart    (14)
i_tap_the_save_button.dart      (9)
i_tap_the_delete_button.dart    (7)
i_tap_the_submit_button.dart    (4)

# 개선: 하나의 파라미터화된 step
# Gherkin: I tap the { ' search ' } button
# Step: iTapTheButton(WidgetTester tester, String buttonName)

Phase 3: 보고서 생성 (--report)#

## BDD Step 재사용성 보고서

### 요약
- 총 step 파일: {N}- 고유 step: {N}- 중복 step: {N}({%}%)
- 제거 가능 파일: {N}개

### Top 20 중복 step
| Step | 중복 횟수 | 공유 라이브러리 후보 |
|------|:---------:|:------------------:|
| ... | ... | Yes/No |

### 표현 정규화 대상
| 현재 | 표준 | 영향 feature 수 |
|------|------|:---------------:|
| ... | ... | ... |

### 파라미터화 후보
| 패턴 | 현재 개별 step 수 | 통합 시 |
|------|:-----------------:|:------:|
| ... | ... | 1|

Phase 4: 자동 수정 (--fix)#

4.1 .feature 파일 정규화

Canonical Step Dictionary에 따라 step 텍스트를 표준화:

# Before
Given the store is loaded
Then the error message should be displayed

# After
Given the store has loaded
Then the error message should be displayed  # (이미 표준)

4.2 중복 step 파일 정리

공유 step과 동일한 구현을 가진 로컬 step 파일 제거 (externalSteps 설정 후):

  1. 공유 step 라이브러리에 canonical step 추가
  2. 각 feature의 build.yamlexternalSteps 추가
  3. 로컬 중복 step 파일 삭제
  4. melos run build 실행하여 생성 코드 갱신
  5. melos run test:bdd 실행하여 테스트 통과 확인

4.3 build_runner 재생성

melos run build:clean  & &   melos run build
melos run test:bdd  # 또는 test:bdd:select

Canonical Step Dictionary#

AI Agent가 .feature 파일 작성 시 반드시 참조해야 하는 표준 step 표현입니다.

Given (Setup) Steps#

표준 표현용도파라미터
I am on the {page} page페이지 설정{page}: snake_case
the {page} has loaded데이터 로드 완료{page}
the {page} is loading로딩 중 상태{page}
the {page} is initialized초기화 완료{page}
the {page} has an error에러 상태{page}
the {page} is empty빈 상태{page}

금지 변형:

  • the X is loadedthe X has loaded
  • the X is in loading statethe X is loading
  • the X has been initializedthe X is initialized

When (Action) Steps — 범용 Key 기반#

표준 표현용도파라미터공유 step
I tap the {'widget_key'} widget 모든 위젯 탭 Widget Key Yes
I tap the {'text'} text 텍스트 기반 탭 화면 텍스트 Yes
I enter {'value'} in the {'key'} widget 모든 입력 값 + Key Yes
I wait for {'N'} seconds 대기 Yes
I scroll down스크롤-No
I upload a file파일 업로드-No

금지 변형 (도메인 특화 step → 범용 step 사용):

  • I tap the search buttonI tap the {'search_button'} widget
  • I tap the save buttonI tap the {'save_button'} widget
  • I select a book filterI tap the {'book_filter'} widget
  • I enter a search keywordI enter {'검색어'} in the {'search_field'} widget
  • I confirm deletionI tap the {'confirm_delete'} widget

Then (Assertion) Steps — 범용 Key 기반#

표준 표현용도파라미터공유 step
the {'widget_key'} widget should be displayed 모든 위젯 표시 확인 Widget Key Yes
the {'text'} text should be displayed 텍스트 표시 확인 화면 텍스트 Yes

금지 변형 (도메인 특화 → 범용 사용):

  • should be visibleshould be displayed (disabled/enabled과 구분)
  • should be shownshould be displayed
  • the X screen should be displayedthe X page should be displayed
  • an error message should be displayedthe error message should be displayed

공유 Step 라이브러리 구조 (co_test_gen 통합)#

공유 step은 test_driver (또는 co_test_gen) 패키지의 shared_step/ 디렉토리에 위치합니다.

package/test_driver/
├── lib/
│   ├── test_driver.dart          # 기존 Driver/Key/Step export
│   ├── shared_steps.dart         # ★ 공유 step barrel export
│   └── src/
│       ├── driver/               # TestDriver 추상화
│       ├── key/                  # Widget Key 상수 (K 클래스)
│       ├── step/                 # 도메인별 Step 클래스
│       └── shared_step/          # ★ BDD 호환 공유 step 함수
│           ├── then/
│           │   ├── the_error_message_should_be_displayed.dart
│           │   ├── the_loading_indicator_should_be_displayed.dart
│           │   └── ... (11)
│           └── when/
│               ├── i_tap_the_search_button.dart
│               ├── i_confirm_deletion.dart
│               └── ... (13)
└── pubspec.yaml

build.yaml 설정 (co_test_gen 사용 시)#

co_test_gen|dual_test_gen:
  enabled: true
  generate_for:
    - test/src/bdd/*.feature
  options:
    stepFolder: step
    sharedSteps: true                                          # ★ 활성화
    sharedStepsImport:  " package:test_driver/shared_steps.dart "   # ★ import 경로
    sharedStepNames:                                           # ★ 매칭 목록
      - the_error_message_should_be_displayed
      - the_loading_indicator_should_be_displayed
      - the_success_message_should_be_displayed
      - the_search_results_should_be_displayed
      - i_enter_a_search_keyword
      - i_confirm_deletion
      - i_tap_the_next_page_button
      - i_tap_the_search_button

/bdd:generate와의 연동#

/bdd:generate 실행 시 자동으로 Canonical Step Dictionary를 참조합니다:

  1. 새 step 작성 전: 기존 step 목록 스캔
  2. 유사 표현 감지: Dictionary와 비교하여 표준 표현 추천
  3. 공유 step 우선: sharedStepNames에 이미 있는 step은 로컬 생성 스킵
  4. 보고: 새로 생성된 step vs 재사용된 step 비율 표시

검증#

# 1. step 중복 검사
find . -path  " */test/src/bdd/step/*.dart "   -exec basename {} \; | sort | uniq -c | sort -rn | head -20

# 2. build 확인
melos run build:clean  & &   melos run build

# 3. BDD 테스트 실행
melos run test:bdd

# 4. 재사용률 측정
total=$(find . -path  " */test/src/bdd/step/*.dart "   | wc -l)
unique=$(find . -path  " */test/src/bdd/step/*.dart "   -exec basename {} \; | sort -u | wc -l)
echo  " 재사용률: $(( (total - unique) * 100 / total ))% "

참조#

  • .claude/rules/bdd-test-patterns.md — BDD 테스트 패턴 규칙
  • .claude/rules/patrol-bdd-conventions.md — Patrol BDD 통합 규칙
  • cc-flutter-dev/commands/bdd/generate.md — BDD 생성 커맨드
  • docs/discovery-bdd-step-reusability.md — Discovery 분석 보고서