LogoSkills

feature-test-bundle

Generate and wire unit (UseCase/BLoC) + BDD dual (widget+Patrol) + E2E scenario three-layer test scaffolds at feature implementation time. Use when kicking off tests for a new feature or running /e...

Feature Test Bundle โ€” unit + BDD dual + E2E 3-layer ๋™์‹œ ์Šค์บํด๋“œ#

ํ•œ๋งˆ๋””๋กœ (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

์ƒˆ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๋ฉด "์ด๊ฒŒ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒ€์‚ฌ ๋„๊ตฌ(ํ…Œ์ŠคํŠธ)"๋„ ๊ฐ™์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํ‚ฌ์€ ๊ทธ ๊ฒ€์‚ฌ ๋„๊ตฌ 3์ข… ์„ธํŠธ๋ฅผ ๋ช…๋ น์–ด ํ•œ ๋ฒˆ์œผ๋กœ ํ•œ๊บผ๋ฒˆ์— ์ค€๋น„ํ•ด ์ฃผ๋Š” ์ž๋™ ์กฐ๋ฆฝ ๋ผ์ธ์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ ์ƒˆ ๊ฐ€์ „์ œํ’ˆ์„ ์ถœ๊ณ ํ•˜๊ธฐ ์ „, ๋ถ€ํ’ˆ ๊ฒ€์‚ฌยท๊ธฐ๋Šฅ ๊ฒ€์‚ฌยท์‹ค์‚ฌ์šฉ ๊ฒ€์‚ฌ๋ฅผ ํ•œ ๋ฒˆ์— ์„ธํŒ…ํ•ด ์ฃผ๋Š” ํ’ˆ์งˆ ๊ด€๋ฆฌ ๋ผ์ธ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฒ€์‚ฌ์˜ 'ํ‹€'์„ ๋งŒ๋“ค์–ด ์ฃผ๊ณ , ์„ธ๋ถ€ ๋‚ด์šฉ์€ ์‚ฌ๋žŒ์ด๋‚˜ ๋‹ค๋ฅธ ๋„๊ตฌ๊ฐ€ ์ฑ„์›Œ ๋„ฃ์Šต๋‹ˆ๋‹ค.

๋ฌด์—‡์„ยท์–ธ์ œ (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

  • ๋ฌด์—‡์„ ํ•ด์ฃผ๋‚˜์š”
    • ์ž‘์€ ๋ถ€ํ’ˆ ๋‹จ์œ„ ๊ฒ€์‚ฌ(Unit), ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์‚ฌ(BDD), ์—ฌ๋Ÿฌ ํ™”๋ฉด์„ ๋„˜๋‚˜๋“œ๋Š” ์‹ค์ œ ํ๋ฆ„ ๊ฒ€์‚ฌ(E2E) ์„ธ ๊ฐ€์ง€ ๊ฒ€์‚ฌ ํ‹€์„ ํ•œ ๋ฒˆ์— ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.
    • ๊ฒ€์‚ฌ ํ‹€๋งŒ ์ž๋™์œผ๋กœ ๋งŒ๋“ค๊ณ , ๋น„์›Œ ๋‘” ๋ถ€๋ถ„(์ฑ„์›Œ์•ผ ํ•  ๊ณณ)์„ ๋ชฉ๋ก์œผ๋กœ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค.
    • ๋งŒ๋“  ์งํ›„ ์ž๋™์œผ๋กœ ํ•œ ๋ฒˆ ๊ฒ€์‚ฌ๋ฅผ ๋Œ๋ ค์„œ, ๋น ์ง„ ๋ถ€๋ถ„์ด ์—†๋Š”์ง€ ๋ฏธ๋ฆฌ ์ ๊ฒ€ํ•ฉ๋‹ˆ๋‹ค.
  • ์–ธ์ œ ์ž‘๋™ํ•˜๋‚˜์š”
    • /e2e:kick-off {๊ธฐ๋Šฅ์ด๋ฆ„} ๋ช…๋ น์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ
    • ์ƒˆ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ๋ง‰ ๋๋‚ด๊ณ  ๊ฒ€์‚ฌ ์ดˆ์•ˆ์„ ๋น ๋ฅด๊ฒŒ ๊ฐ–์ถ”๊ณ  ์‹ถ์„ ๋•Œ
    • ์ฝ”๋“œ ์ œ์ถœ(PR) ์ „์— "๊ฒ€์‚ฌ 3์ข…์ด ๋ชจ๋‘ ๊ฑธ๋ ค ์žˆ๋Š”์ง€" ํ™•์ธํ•˜๊ณ  ์‹ถ์„ ๋•Œ

ํ•ต์‹ฌ ์šฉ์–ด (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

์šฉ์–ด์‰ฌ์šด ์„ค๋ช…
ํ…Œ์ŠคํŠธ(test)์ฝ”๋“œ๊ฐ€ ์˜๋„๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ์ž๋™์œผ๋กœ ๊ฒ€์‚ฌํ•˜๋Š” ์ ˆ์ฐจ
Unit(๋‹จ์œ„) ํ…Œ์ŠคํŠธ๊ฐ€์žฅ ์ž‘์€ ๋ถ€ํ’ˆ ํ•˜๋‚˜ํ•˜๋‚˜๊ฐ€ ์ œ๋Œ€๋กœ ๋„๋Š”์ง€ ๋ณด๋Š” ๊ฒ€์‚ฌ
BDD"์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค"๋Š” ์‚ฌ์šฉ์ž ํ–‰๋™ ๋ฌธ์žฅ์œผ๋กœ ์ ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์‚ฌ ๋ฐฉ์‹
E2E์‹œ์ž‘๋ถ€ํ„ฐ ๋๊นŒ์ง€, ์—ฌ๋Ÿฌ ํ™”๋ฉด์„ ๋„˜๋‚˜๋“œ๋Š” ์‹ค์ œ ์‚ฌ์šฉ ํ๋ฆ„์„ ํ†ต์งธ๋กœ ๋ณด๋Š” ๊ฒ€์‚ฌ
์Šค์บํด๋“œ(scaffold)๋‚ด์šฉ์€ ๋น„์–ด ์žˆ์ง€๋งŒ ๋ผˆ๋Œ€๋งŒ ๊ฐ–์ถ˜ ํ‹€(์‹œ์ž‘์šฉ ์–‘์‹)
feature(๊ธฐ๋Šฅ)์•ฑ์—์„œ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ ๋ฌถ์Œ(์˜ˆ: ๋กœ๊ทธ์ธ, ๊ฒ€์ƒ‰)
.feature ํŒŒ์ผ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฌ์šด ๋ฌธ์žฅ์œผ๋กœ ์ ์–ด ๋‘” ํŒŒ์ผ
step(์Šคํ…)์‹œ๋‚˜๋ฆฌ์˜ค ๋ฌธ์žฅ ํ•˜๋‚˜๋ฅผ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜๋Š” ์ž‘์€ ๋™์ž‘ ์ •์˜
Patrol์‹ค์ œ ์•ฑ์„ ์ผœ์„œ ์‚ฌ๋žŒ์ฒ˜๋Ÿผ ๋ˆŒ๋Ÿฌ ๋ณด๋ฉฐ ๊ฒ€์‚ฌํ•˜๋Š” ๋„๊ตฌ
widget(์œ„์ ฏ)Flutter ์•ฑ ํ™”๋ฉด์„ ์ด๋ฃจ๋Š” UI ์กฐ๊ฐ
mock(๋ชฉ)์ง„์งœ ๋Œ€์‹  ๋ผ์›Œ ๋„ฃ๋Š” ๊ฐ€์งœ ๋ถ€ํ’ˆ(๊ฒ€์‚ฌ์šฉ ๋Œ€์—ญ)
๋กœ์ปฌ ํ’€์Šคํƒ๋‚ด ์ปดํ“จํ„ฐ์—์„œ ์„œ๋ฒ„ยทDBยท์•ฑ์„ ํ•œ๊บผ๋ฒˆ์— ๋„์›Œ ํ†ต์งธ๋กœ ๋Œ๋ ค ๋ณด๋Š” ํ™˜๊ฒฝ
PR์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ•ฉ์น˜์ž๊ณ  ์˜ฌ๋ฆฌ๋Š” ๋ณ€๊ฒฝ ์š”์ฒญ(Pull Request)
TODO"์—ฌ๊ธฐ๋Š” ๋‚˜์ค‘์— ์ฑ„์›Œ์•ผ ํ•จ"์ด๋ผ๊ณ  ํ‘œ์‹œํ•ด ๋‘” ๋นˆ์นธ
์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์—ฌ๋Ÿฌ ๋„๊ตฌยท๋‹จ๊ณ„๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ฌถ์–ด ์ง€ํœ˜ํ•˜๋Š” ์ผ

๋ชฉ์ #

๊ธฐ๋Šฅ ๊ตฌํ˜„ ์™„๋ฃŒ ์งํ›„ ๋‹ค์Œ์„ ํ•˜๋‚˜์˜ ๋ช…๋ น ์œผ๋กœ ์ค€๋น„:

  1. Unit ๋ ˆ์ด์–ด โ€” UseCase, Repository, BLoC ๋‹จ์œ„ ํ…Œ์ŠคํŠธ (Mocktail + bloc_test)
  2. BDD Dual ๋ ˆ์ด์–ด โ€” .feature + TestDriver step + bdd_test_gen โ†’ *.widget_test.dart + *.patrol_test.dart
  3. E2E ์‹œ๋‚˜๋ฆฌ์˜ค ๋ ˆ์ด์–ด โ€” cross-feature ๊ฐ€ ํ•„์š”ํ•˜๋ฉด integration_test/features/ ๋กœ ์ด๊ด€ + test_bundle.dart ๋“ฑ๋ก

๊ฐ ๋ ˆ์ด์–ด๋Š” ๊ธฐ์กด ์กฐ์ง ์Šคํ‚ฌ์— ์œ„์ž„ โ€” ๋ณธ ์Šคํ‚ฌ์€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜๋งŒ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

ํŠธ๋ฆฌ๊ฑฐ#

  • /e2e:kick-off {feature} ์‹คํ–‰
  • ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์งํ›„ ํ…Œ์ŠคํŠธ ์ดˆ์•ˆ์„ ๋น ๋ฅด๊ฒŒ ๊ฐ–์ถ”๊ณ  ์‹ถ์„ ๋•Œ
  • PR ์ œ์ถœ ์ „์— "ํ…Œ์ŠคํŠธ 3 ๋ ˆ์ด์–ด๊ฐ€ ๋ชจ๋‘ ๊ฑธ๋ ค ์žˆ๋Š”์ง€" ํ™•์ธํ•  ๋•Œ

์ „์ œ#

  • feature ๋ชจ๋“ˆ ์ด๋ฏธ ์กด์žฌ (feature/{type}/{name}/) โ€” ์—†์œผ๋ฉด cc-bricks-feature ๋˜๋Š” cc-flutter-dev:feature:* ๋กœ ๋จผ์ € scaffold
  • ํ•ด๋‹น feature ์˜ ๋„๋ฉ”์ธ/๋ฐ์ดํ„ฐ/ํ”„๋ ˆ์  ํ…Œ์ด์…˜ 3 ๋ ˆ์ด์–ด ์ฝ”๋“œ๊ฐ€ ์ตœ์†Œ ์ž‘์„ฑ๋จ
  • package/test_driver, bdd_test_gen ์˜์กด์„ฑ pubspec ์— ๋ฐ˜์˜
  • (์„ ํƒ) flutter-skill MCP ์‚ฌ์šฉ ๊ฐ€๋Šฅ โ€” ํƒ์ƒ‰ ๋‹จ๊ณ„ ์ˆ˜ํ–‰ ์‹œ์—๋งŒ ํ•„์š”

๋™์ž‘#

1. ์‚ฌ์ „ ์กฐ์‚ฌ#

  1. feature/{type}/{name}/lib/ ๋ฅผ ์ฝ์–ด UseCase, BLoC, Repository ๋ชฉ๋ก ์ˆ˜์ง‘
  2. feature/{type}/{name}/test/src/ ์— ๊ธฐ์กด ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์กฐ์‚ฌ โ€” ์ค‘๋ณต ์ƒ์„ฑ ๋ฐฉ์ง€
  3. feature/{type}/{name}/test/src/bdd/{name}.feature ์œ ๋ฌด ํŒ๋‹จ
  4. .claude/e2e-lock.json ์—์„œ ํ•ด๋‹น feature ์˜ lock ๋œ ์‹œ๋‚˜๋ฆฌ์˜ค ํŒŒ์•… (์žฌ์ €์ž‘ ๋ฐฉ์ง€)

2. Unit ๋ ˆ์ด์–ด ์Šค์บํด๋“œ (cc-uiux:unit-testing ์œ„์ž„)#

๊ฐ UseCase / BLoC / Repository ์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ:

  • UseCase: test/src/domain/usecase/{use_case_name}_test.dart

    • Mocktail ๋กœ Repository mock
    • should return entity when repository succeeds / should return failure when repository fails ๊ธฐ๋ณธ 2 ํ…Œ์ŠคํŠธ
  • BLoC: test/src/presentation/bloc/{bloc_name}_test.dart

    • bloc_test ์˜ blocTest() ์‚ฌ์šฉ
    • emits [Loading, Loaded] / emits [Loading, Error] ๊ธฐ๋ณธ ์‹œ๋‚˜๋ฆฌ์˜ค
  • Repository: test/src/data/repository/{repository_name}_test.dart

    • DataSource mock
    • ์ฃผ์š” ๋ฉ”์„œ๋“œ๋ณ„ ์„ฑ๊ณต/์‹คํŒจ ์ผ€์ด์Šค

์‹ค์ œ ์Šค์บํด๋“œ๋Š” cc-flutter-dev/commands/test.md ์˜ /test unit {target} ํ…œํ”Œ๋ฆฟ์„ ํ˜ธ์ถœํ•˜์—ฌ ์ผ๊ด€์„ฑ ์œ ์ง€.

3. BDD ๋ ˆ์ด์–ด ์Šค์บํด๋“œ#

3a. .feature ํŒŒ์ผ ํ™•๋ณด

flag { .feature ์กด์žฌ ์—ฌ๋ถ€ }
โ”œโ”€โ”€ ์กด์žฌ โ†’ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ (๊ฒฝ๊ณ  ์ฃผ์„ ์ œ๊ฑฐ ์ƒํƒœ์—ฌ์•ผ ํ•จ)
โ””โ”€โ”€ ์—†์Œ
    โ”œโ”€โ”€ flutter-skill ์„ธ์…˜ ์‹คํ–‰ ๊ฐ€๋Šฅ โ†’ /e2e:explore โ†’ /e2e:draft ์—ฐ์† ํ˜ธ์ถœ ์ œ์•ˆ
    โ””โ”€โ”€ ์•„๋‹ˆ๋ฉด โ†’ ์ตœ์†Œ skeleton ์ƒ์„ฑ (์˜์–ด + ํ•œ๊ธ€ ์ฃผ์„ ํฌ๋งท, @both ํƒœ๊ทธ)

Skeleton ์˜ˆ์‹œ:

# DRAFT โ€” auto-generated skeleton by feature-test-bundle
# generated: {timestamp}
# โš ๏ธ Replace placeholder steps with actual scenarios before /e2e:lock.

@smoke
@{feature_domain}
Feature: {Feature PascalCase} # {ํ•œ๊ธ€ ์ด๋ฆ„}
  {one-line user value} # {ํ•œ๊ธ€ ๋ฒˆ์—ญ}

  Background:
    Given I am on the {feature} page # {feature} ํŽ˜์ด์ง€์— ์žˆ๋‹ค

  @both
  Scenario: Happy path for {feature} # {feature} ์ •์ƒ ํ๋ฆ„
    When I tap the {primary_action} button # {primary_action} ๋ฒ„ํŠผ ํƒญ
    Then the {expected_state} should be displayed # {expected_state} ํ‘œ์‹œ

3b. TestDriver step ํŒŒ์ผ ์Šค์บํด๋“œ

.feature ์˜ Given/When/Then ์„ ํŒŒ์‹ฑํ•ด ๊ฐ ๋ฌธ์žฅ์— ๋Œ€์‘ํ•˜๋Š” step ํŒŒ์ผ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ:

  • ํŒŒ์ผ๋ช…: snake_case ์˜์–ด (i_tap_the_sign_in_button.dart)
  • ํ•จ์ˆ˜๋ช…: camelCase (iTapTheSignInButton)
  • ๋ณธ๋ฌธ์€ TestDriver ์ธ์ž + TODO ์ฃผ์„ (์‚ฌ๋žŒ์ด ์ฑ„์›€)
  • ํ•„์š”ํ•œ K ์ƒ์ˆ˜๊ฐ€ package/test_driver/lib/src/key/k.dart ์— ์—†์œผ๋ฉด key-harden-targets.md ์— ์ถ”๊ฐ€ ๋“ฑ๋ก

3c. build.yaml ํ™•์ธยท๊ฐฑ์‹ 

ํ•ด๋‹น feature ํŒจํ‚ค์ง€์˜ build.yaml ์— bdd_test_gen|dual_test_gen ํ™œ์„ฑ ํ™•์ธ. ์—†์œผ๋ฉด ํ‘œ์ค€ ๋ธ”๋ก ์ถ”๊ฐ€:

targets:
  $default:
    builders:
      bdd_test_gen|dual_test_gen:
        enabled: true
        generate_for:
          - test/src/bdd/*.feature
        options:
          stepFolder: step

3d. ์ฝ”๋“œ ์ƒ์„ฑ

cd feature/{type}/{name}
dart run build_runner build --delete-conflicting-outputs

๊ฒฐ๊ณผ: *.widget_test.dart + *.patrol_test.dart (ํ•„์š” ์‹œ *.widget_test.dart ๋งŒ).

๋ฐฑ์—”๋“œ ์ •์ฑ… โ€” Patrol/E2E ๋ ˆ์ด์–ด ๊ธฐ๋ณธ๊ฐ’ = ๋กœ์ปฌ ํ’€์Šคํƒ. ์ž๋™ ์ƒ์„ฑ๋˜๋Š” *.patrol_test.dart (BDD dual ์˜ Patrol ์ธก) ์™€ ์•„๋ž˜ 4 ์˜ cross-feature E2E ๋ ˆ์ด์–ด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กœ์ปฌ ํ’€์Šคํƒ ๋ฐฑ์—”๋“œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•œ๋‹ค โ€” serverpod start ํ•œ ๋ช…๋ น์ด ๋ฐฑ์—”๋“œ ์„œ๋ฒ„ + ๋‚ด์žฅ Postgres + Flutter ์•ฑ์„ ํ•จ๊ป˜ ๊ธฐ๋™ํ•˜๋ฉฐ(Dockerยทdocker-compose ๋ถˆํ•„์š”), ์•ฑ์€ dev flavor ์—์„œ http://localhost:8080/ ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค. ํ•ซ๋ฆฌ๋กœ๋“œ๊ฐ€ ๋ง‰ํžˆ๋ฉด serverpod start ํ„ฐ๋ฏธ๋„์—์„œ R ํ‚ค๋กœ ์„œ๋ฒ„+์•ฑ ๊ฐ•์ œ ์žฌ์‹œ์ž‘. Staging ์€ ํด๋ฐฑ(nightly/CI ๋˜๋Š” ๋กœ์ปฌ ํ’€์Šคํƒ ๊ธฐ๋™ ๋ถˆ๊ฐ€ ์‹œ)์ด๋ฉฐ ์•ฑ์˜ ๊ธฐ์กด client/OpenApiService/flavor ํด๋ฐฑ ์„ค์ •์„ ๋”ฐ๋ฅธ๋‹ค โ€” base-URL ํ•˜๋“œ์ฝ”๋”ฉ ๊ธˆ์ง€. unit + widget ๋ ˆ์ด์–ด์˜ mock ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ (์œ„ 2 / BDD widget_test ์˜ mock BLoC). ์ด ๋…ธํŠธ๋Š” Patrolยท integration ๋ ˆ์ด์–ด์—๋งŒ ์ ์šฉ๋œ๋‹ค. ์…‹์—…ยทMCP ์—ฐ๋™์€ serverpod-local-fullstack, serverpod-mcp-guide ์Šคํ‚ฌ ์ฐธ์กฐ. (3.x / legacy: ๋กœ์ปฌ DB ๋Š” docker compose up -d ๋กœ ๊ธฐ๋™, ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋Š” withServerpod + dart test -t integration โ€” API ๋™์ผ.)

4. E2E ์‹œ๋‚˜๋ฆฌ์˜ค ๋ ˆ์ด์–ด (์„ ํƒ์ )#

.feature ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค ์ค‘ cross-feature ๋˜๋Š” @patrol-only @journey ๋กœ ํŒ๋‹จ๋˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค:

  1. app/{app}/integration_test/features/{name}.feature ๋กœ ์ด๊ด€
  2. integration_test/scenarios/{name}_{scenario_slug}_test.dart ์ƒ์„ฑ
  3. test_bundle.dart ์— import ์ถ”๊ฐ€
  4. run_all_scenarios.sh ๊ฐฑ์‹ 

ํŒ๋‹จ ํœด๋ฆฌ์Šคํ‹ฑ:

  • @journey ํƒœ๊ทธ
  • step ์—์„œ 2๊ฐœ ์ด์ƒ feature ์˜ ํŽ˜์ด์ง€ ์ „์ด
  • ์‹ค ๋ฐฑ์—”๋“œ ์—ฐ๋™ ํ•„์š” (๊ธฐ๋ณธ = ๋กœ์ปฌ ํ’€์Šคํƒ serverpod start + ๋‚ด์žฅ Postgres, Staging ํด๋ฐฑ โ†’ serverpod-local-fullstack)

5. ๊ฒ€์ฆ (/pr:preflight ์œ„์ž„)#

์Šค์บํด๋“œ ์ƒ์„ฑ ์งํ›„ cc-dev-cycle:pr:preflight --scope={feature-packages} ๋ฅผ ์ž๋™ ํ˜ธ์ถœ:

๋ ˆ์ด์–ด/pr:preflight ์‹คํ–‰ ๋ช…๋ น์ฑ…์ž„
Unit melos exec --scope=... -- flutter test --exclude-tags=integration /pr:preflight
Widget (BDD widget_test ํฌํ•จ)์œ„ ๋ช…๋ น์— ํฌํ•จ/pr:preflight
Integration @smoke (์กฐ๊ฑด๋ถ€) patrol test --target=@smoke (๋””๋ฐ”์ด์Šค ์žˆ์„ ๋•Œ) /pr:preflight

๊ฐ ๋ ˆ์ด์–ด ์‹คํŒจ ์›์ธ์€ preflight ๋ฆฌํฌํŠธ์— ๊ทธ๋ฃนํ™”ํ•ด ์ œ์‹œ. /pr:preflight ์˜ ์ •์ฑ…์€ cc-dev-cycle/rules/pr-preflight-policy.md ์ฐธ์กฐ.

6. ๋ฆฌํฌํŠธ#

์‚ฌ์šฉ์ž์—๊ฒŒ ์ถœ๋ ฅ:

  • ์ƒ์„ฑ๋œ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ˆ˜ (๋ ˆ์ด์–ด๋ณ„)
  • TODO ์ฃผ์„์ด ๋‚จ์€ step ํŒŒ์ผ ๋ชฉ๋ก (์‚ฌ๋žŒ์ด ์ฑ„์›Œ์•ผ ํ•จ)
  • ๋‹ค์Œ ๋‹จ๊ณ„ ์ œ์•ˆ:
    • TODO ํ•ด๊ฒฐ โ†’ /e2e:explore ๋กœ ์‹ค์ œ ์กฐ์ž‘ ํ™•์ธ โ†’ /e2e:draft โ†’ /e2e:lock
    • ๋˜๋Š” ๋น ๋ฅธ ๊ฒฝ๋กœ: cc-flutter-dev:test ์—์ด์ „ํŠธ๋กœ unit/widget ์™„์„ฑ

์ถœ๋ ฅ#

Unit ๋ ˆ์ด์–ด#

  • feature/{type}/{name}/test/src/domain/usecase/*_test.dart
  • feature/{type}/{name}/test/src/presentation/bloc/*_test.dart
  • feature/{type}/{name}/test/src/data/repository/*_test.dart

BDD Dual ๋ ˆ์ด์–ด#

  • feature/{type}/{name}/test/src/bdd/{name}.feature (์‹ ๊ทœ or ๊ธฐ์กด)
  • feature/{type}/{name}/test/src/bdd/step/*.dart โ€” TestDriver step ํ…œํ”Œ๋ฆฟ
  • feature/{type}/{name}/test/src/bdd/{name}.widget_test.dart โ€” ์ž๋™ ์ƒ์„ฑ
  • feature/{type}/{name}/test/src/bdd/{name}.patrol_test.dart โ€” ์ž๋™ ์ƒ์„ฑ
  • feature/{type}/{name}/build.yaml โ€” bdd_test_gen ์„ค์ • ํ™•์ธ

E2E ์‹œ๋‚˜๋ฆฌ์˜ค ๋ ˆ์ด์–ด (์„ ํƒ)#

  • app/{app}/integration_test/features/{name}.feature
  • app/{app}/integration_test/scenarios/{name}_{scenario}_test.dart
  • test_bundle.dart, run_all_scenarios.sh ์—…๋ฐ์ดํŠธ

์œ„์ž„ ๋งคํŠธ๋ฆญ์Šค#

๋ ˆ์ด์–ด์œ„์ž„ ๋Œ€์ƒํ˜ธ์ถœ ๋ฐฉ์‹
Unit - UseCase cc-uiux:unit-testing /test unit {UseCase} --feature {name}
Unit - BLoC cc-uiux:unit-testing + cc-flutter-dev:bloc /test bloc {Bloc} --feature {name}
Unit - Repository cc-uiux:unit-testing /test unit {Repository} --feature {name}
Widget ๊ฐœ๋ณ„ cc-uiux:widget-testing /test widget {Widget} --feature {name}
BDD Dual cc-uiux:bdd-testing + cc-flutter-dev:bdd/generate ๋ณธ ์Šคํ‚ฌ์ด bdd_test_gen ์‹คํ–‰
Patrol step ๊ตฌํ˜„ cc-e2e-authoring:step-implement + patrol_mcp ํ›„์† ๋‹จ๊ณ„
๊ณต์œ  step ์ถ”์ถœ cc-flutter-dev:bdd-step-reuse-agent 2+ feature ์—์„œ ๋ฐ˜๋ณต๋˜๋Š” step
๊ฒ€์ฆ(Unit+Widget+Integration@smoke) cc-dev-cycle:pr:preflight ์Šค์บํด๋“œ ์งํ›„ ์ž๋™ ํ˜ธ์ถœ, ์‹คํŒจ ์‹œ PR ์ฐจ๋‹จ

๊ธˆ์ง€ ์‚ฌํ•ญ#

  • โŒ ๊ธฐ์กด ์กฐ์ง ์Šคํ‚ฌ(cc-uiux:*, cc-flutter-dev:*) ์˜ ํ…œํ”Œ๋ฆฟ์„ ์žฌ์ •์˜
  • โŒ .widget_test.dart, .patrol_test.dart ์ž๋™ ์ƒ์„ฑ๋ฌผ ์ˆ˜๋™ ํŽธ์ง‘
  • โŒ .feature ๋ฅผ Korean-only ๋กœ ์ƒ์„ฑ (์˜์–ด + ํ•œ๊ธ€ ์ฃผ์„ ํ•„์ˆ˜)
  • โŒ flutter-skill MCP ๋ฅผ ๋ณธ ์Šคํ‚ฌ ๋‚ด์—์„œ ์ง์ ‘ ํ˜ธ์ถœ โ€” /e2e:explore ์ปค๋งจ๋“œ๋ฅผ ํ†ตํ•ด์„œ๋งŒ
  • โŒ lock ๋œ feature ์˜ ๊ธฐ์กด ์‹œ๋‚˜๋ฆฌ์˜ค ๋ฎ์–ด์“ฐ๊ธฐ โ€” draft ๋กœ ๋ณ„๋„ ์ƒ์„ฑ

์‹คํŒจ ์ผ€์ด์Šค#

์›์ธ๋Œ€์‘
feature ๋ชจ๋“ˆ ์—†์Œ cc-bricks-feature / cc-flutter-dev:feature:* ์•ˆ๋‚ด
bdd_test_gen ๋นŒ๋“œ ์‹คํŒจstep ํŒŒ์ผ๋ช…/ํ•จ์ˆ˜๋ช… ๋งคํ•‘ ์žฌ๊ฒ€ํ† , dart analyze
K ์ƒ์ˆ˜ ๋ˆ„๋ฝkey-hardening ์žฌ์‹คํ–‰ ์•ˆ๋‚ด
.feature skeleton ๋งŒ์œผ๋กœ๋Š” ํƒ์ƒ‰ ๋ถ€์กฑ/e2e:explore ๋กœ ์‹ค์ œ ์•ฑ ํƒ์ƒ‰ ๊ถŒ์œ 
cross-feature ํŒ๋‹จ ์• ๋งค์‚ฌ์šฉ์ž ์„ ํƒ์ง€ (feature ๋ชจ๋“ˆ / app integration_test)

์ฐธ๊ณ #

  • ์กฐ์ง ํ…Œ์ŠคํŠธ ์›์ „:
    • cc-uiux/skills/unit-testing/SKILL.md
    • cc-uiux/skills/widget-testing/SKILL.md
    • cc-uiux/skills/bdd-testing/SKILL.md
    • cc-uiux/skills/integration-testing/SKILL.md
  • ํŒจํ„ด ๋ฃฐ: cc-flutter-dev/rules/bdd-test-patterns.md
  • ๊ณต์œ  step: cc-flutter-dev/agents/bdd-step-reuse-agent.md
  • ๋กœ์ปฌ ํ’€์Šคํƒ ๋ฐฑ์—”๋“œ(Patrol/E2E ๊ธฐ๋ณธ): serverpod-local-fullstack, serverpod-mcp-guide
  • ๋ณธ ํ”Œ๋Ÿฌ๊ทธ์ธ ์—ฐ๊ณ„: skills/exploration, skills/scenario-draft, skills/key-hardening, skills/step-implement, skills/scenario-lock