| 항목 | 내용 |
|---|---|
| Invoke | /bdd:refactor-steps |
| Aliases | /bdd:refactor, /bdd:step-audit |
| Category | test-quality |
| Complexity | moderate |
/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#
| Parameter | Required | Description | Example |
|---|---|---|---|
feature_name |
No | 특정 feature 분석 (생략 시 전체) | store |
--fix | No | 분석 + 자동 수정 | true |
--report | No | 분석 보고서만 출력 | true |
--scope |
No | feature 범위 제한 | application, console, common |
--dry-run | No | 변경 내용 미리보기 (수정 없음) | 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 loaded | the X has loaded | tense 통일 |
the X is in loading state | the 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 button | I tap the X button | click→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 설정 후):
- 공유 step 라이브러리에 canonical step 추가
- 각 feature의
build.yaml에externalSteps추가 - 로컬 중복 step 파일 삭제
melos run build실행하여 생성 코드 갱신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를 참조합니다:
- 새 step 작성 전: 기존 step 목록 스캔
- 유사 표현 감지: Dictionary와 비교하여 표준 표현 추천
- 공유 step 우선:
sharedStepNames에 이미 있는 step은 로컬 생성 스킵 - 보고: 새로 생성된 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 분석 보고서