LogoSkills

bdd-refactor-steps

프로젝트에 흩어진 BDD step 문장의 중복·유사 표현을 찾아 재사용성 보고서를 내고, `--fix` 로 `.feature` 를 표준 표현으로 바꾸고 중복 step 파일을 정리한 뒤 테스트 통과까지 확인합니다.

/cc-flutter:bdd:refactor-steps — 테스트 문장 중복 정리 도우미#

항목내용
실행 명령/cc-flutter:bdd:refactor-steps
분류테스트 품질
난이도●●○ 보통

한마디로#

여러 테스트에 흩어진 같은 뜻의 문장을 하나의 표준 문장으로 정리해 주는 명령입니다. 회사 문서마다 제각각 쓰던 표현("확인 바랍니다" / "확인 부탁드립니다" / "체크 요망")을 하나의 공식 표현으로 통일하는 것과 같아요.

누가·언제 쓰나요#

  • 테스트 코드를 정리·유지보수할 때
  • 새 기능 테스트를 작성하기 전에, 이미 있는 문장을 재사용할 수 있는지 점검할 때
  • 같은 테스트 문장이 여기저기 중복된 것 같을 때
  • 새 테스트를 생성하는 /cc-flutter:bdd:generate 명령을 돌리기 전 사전 점검용으로

무엇을 해주나요#

  • 프로젝트 전체에 흩어진 테스트 문장(step 파일)을 모아 중복·유사 표현을 찾아냅니다.
  • 어떤 문장이 몇 번 중복됐는지, 표준으로 바꿀 대상은 무엇인지 정리한 재사용성 보고서를 만들어 줍니다.
  • --fix 모드에서는 .feature 파일의 문장을 표준 표현으로 바꾸고, 중복된 step 파일을 정리한 뒤, 테스트가 그대로 잘 통과하는지까지 확인합니다.

어떻게 쓰나요#

/cc-flutter:bdd:refactor-steps                          # 전체 프로젝트 분석
/cc-flutter:bdd:refactor-steps store                    # 특정 feature만 분석
/cc-flutter:bdd:refactor-steps --fix                    # 자동 수정 모드
/cc-flutter:bdd:refactor-steps --report                 # 보고서만 출력
/cc-flutter:bdd:refactor-steps --scope console          # 콘솔 feature만
  • 뒤에 store 같은 이름을 붙이면 그 기능 하나만 봅니다 (생략하면 전체).
  • --report는 고치지 않고 보고서만, --fix는 분석 후 실제 수정까지, --dry-run은 바꿀 내용을 미리보기만 합니다.
  • --scope로 범위를 좁힐 수 있습니다 (application, console, common).

안에서 무슨 일이 벌어지나요#

  1. 모으기 — 프로젝트 전체의 테스트 문장 파일을 긁어모아 목록(인벤토리)을 만듭니다.
  2. 중복 찾기 — 완전히 똑같은 문장, 살짝만 다른 비슷한 문장, "버튼 이름만 다른" 문장 등을 분류합니다.
  3. 보고서 만들기 — 중복 횟수, 표준화 대상, 하나로 합칠 후보를 표로 정리합니다.
  4. 자동 수정(--fix) — 표준 표현으로 문장을 바꾸고, 중복 파일을 공용 라이브러리로 옮긴 뒤 정리하고, 빌드와 테스트를 다시 돌려 이상 없는지 확인합니다.

⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)

Triggers#

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

사용법#

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

Parameters#

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

실행 흐름#

Phase 1: Step 인벤토리 수집#

프로젝트 전체의 step 파일을 스캔하여 인벤토리를 구축합니다. BDD 관련 파일은 feature 패키지가 아니라 앱별 app/{app}/integration_test/step/ 한 곳에만 있습니다(정본: plugins/cc-flutter/skills/bdd-testing/SKILL.md → "디렉토리 구조").

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

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

# 3. 앱별 step 수 집계
for dir in app/*/; do
  count=$(find  " $dir "   -path  " */integration_test/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 displayedthe error message should be displayed관사 통일
the book detail screen should be displayedthe book detail page should be displayedscreen→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(TestDriver driver, 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 파일 제거 (직접 import로 교체 후):

  1. 공유 step 라이브러리(package/test_driver/lib/shared_steps.dart)에 canonical step 추가
  2. 로컬 중복 step 파일을 참조하던 app/{app}/integration_test/scenarios/*_test.dart의 호출부를 공유 step 직접 import(import 'package:test_driver/shared_steps.dart';)로 교체
  3. 로컬 중복 step 파일 삭제
  4. patrol test --target integration_test/scenarios/{name}_test.dart 실행하여 테스트 통과 확인

4.3 검증 재실행

melos run build / melos run test:bdd(:select) 는 폐지된 코드생성 파이프라인 명령입니다 — BDD에는 재실행할 빌드가 없습니다.

cd app/{app}
patrol test --target integration_test/scenarios/{name}_test.dart
# 전수 순차 실행
./integration_test/run_all_scenarios.sh

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 KeyYes
I tap the {'text'} text텍스트 기반 탭화면 텍스트Yes
I enter {'value'} in the {'key'} widget모든 입력값 + KeyYes
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 KeyYes
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 라이브러리 구조#

공유 step은 test_driver 패키지의 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

공유 Step Import (build.yaml 없음)#

co_test_gen/bdd_test_gen(|dual_test_gen) 빌더 설정은 더 이상 없습니다 — BDD 파이프라인 전체가 코드 생성을 쓰지 않으므로 sharedStepNames 로 등록할 대상 자체가 없습니다. 공유 step을 쓰려면 app/{app}/integration_test/scenarios/{name}_{scenario}_test.dart 상단에서 직접 import합니다:

import 'package:test_driver/shared_steps.dart';

shared_steps.dart 의 export 목록에 있는 이름은 전부 이렇게 바로 호출 가능합니다 — the_error_message_should_be_displayed, the_loading_indicator_should_be_displayed, the_success_message_should_be_displayed, i_confirm_deletion, i_tap_the_next_page_button 등 (전체 목록은 bdd-canonical-steps 참조).


/cc-flutter:bdd:generate와의 연동#

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

  1. 새 step 작성 전: 기존 step 목록 스캔
  2. 유사 표현 감지: Dictionary와 비교하여 표준 표현 추천
  3. 공유 step 우선: shared_steps.dart에 이미 있는 이름이면 로컬 파일을 만들지 않고 직접 import
  4. 보고: 새로 작성된 step vs 재사용된 step 비율 표시

검증#

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

# 2. Patrol E2E 실행 (⛔ melos run build / melos run test:bdd 는 폐지 — 재생성할 빌드가 없다)
cd app/{app}
patrol test --target integration_test/scenarios/{name}_test.dart

# 3. 재사용률 측정
total=$(find app -path  " */integration_test/step/*.dart "   | wc -l)
unique=$(find app -path  " */integration_test/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/commands/bdd/generate.md — BDD 생성 커맨드
  • docs/discovery-bdd-step-reusability.md — Discovery 분석 보고서