Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

selective-testing.md 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. # Selective and Targeted Test Execution
  2. ## Principle
  3. Run only the tests you need, when you need them. Use tags/grep to slice suites by risk priority (not directory structure), filter by spec patterns or git diff to focus on impacted areas, and combine priority metadata (P0-P3) with change detection to optimize pre-commit vs. CI execution. Document the selection strategy clearly so teams understand when full regression is mandatory.
  4. ## Rationale
  5. Running the entire test suite on every commit wastes time and resources. Smart test selection provides fast feedback (smoke tests in minutes, full regression in hours) while maintaining confidence. The "32+ ways of selective testing" philosophy balances speed with coverage: quick loops for developers, comprehensive validation before deployment. Poorly documented selection leads to confusion about when tests run and why.
  6. ## Pattern Examples
  7. ### Example 1: Tag-Based Execution with Priority Levels
  8. **Context**: Organize tests by risk priority and execution stage using grep/tag patterns.
  9. **Implementation**:
  10. ```typescript
  11. // tests/e2e/checkout.spec.ts
  12. import { test, expect } from '@playwright/test';
  13. /**
  14. * Tag-based test organization
  15. * - @smoke: Critical path tests (run on every commit, < 5 min)
  16. * - @regression: Full test suite (run pre-merge, < 30 min)
  17. * - @p0: Critical business functions (payment, auth, data integrity)
  18. * - @p1: Core features (primary user journeys)
  19. * - @p2: Secondary features (supporting functionality)
  20. * - @p3: Nice-to-have (cosmetic, non-critical)
  21. */
  22. test.describe('Checkout Flow', () => {
  23. // P0 + Smoke: Must run on every commit
  24. test('@smoke @p0 should complete purchase with valid payment', async ({ page }) => {
  25. await page.goto('/checkout');
  26. await page.getByTestId('card-number').fill('4242424242424242');
  27. await page.getByTestId('submit-payment').click();
  28. await expect(page.getByTestId('order-confirmation')).toBeVisible();
  29. });
  30. // P0 but not smoke: Run pre-merge
  31. test('@regression @p0 should handle payment decline gracefully', async ({ page }) => {
  32. await page.goto('/checkout');
  33. await page.getByTestId('card-number').fill('4000000000000002'); // Decline card
  34. await page.getByTestId('submit-payment').click();
  35. await expect(page.getByTestId('payment-error')).toBeVisible();
  36. await expect(page.getByTestId('payment-error')).toContainText('declined');
  37. });
  38. // P1 + Smoke: Important but not critical
  39. test('@smoke @p1 should apply discount code', async ({ page }) => {
  40. await page.goto('/checkout');
  41. await page.getByTestId('promo-code').fill('SAVE10');
  42. await page.getByTestId('apply-promo').click();
  43. await expect(page.getByTestId('discount-applied')).toBeVisible();
  44. });
  45. // P2: Run in full regression only
  46. test('@regression @p2 should remember saved payment methods', async ({ page }) => {
  47. await page.goto('/checkout');
  48. await expect(page.getByTestId('saved-cards')).toBeVisible();
  49. });
  50. // P3: Low priority, run nightly or weekly
  51. test('@nightly @p3 should display checkout page analytics', async ({ page }) => {
  52. await page.goto('/checkout');
  53. const analyticsEvents = await page.evaluate(() => (window as any).__ANALYTICS__);
  54. expect(analyticsEvents).toBeDefined();
  55. });
  56. });
  57. ```
  58. **package.json scripts**:
  59. ```json
  60. {
  61. "scripts": {
  62. "test": "playwright test",
  63. "test:smoke": "playwright test --grep '@smoke'",
  64. "test:p0": "playwright test --grep '@p0'",
  65. "test:p0-p1": "playwright test --grep '@p0|@p1'",
  66. "test:regression": "playwright test --grep '@regression'",
  67. "test:nightly": "playwright test --grep '@nightly'",
  68. "test:not-slow": "playwright test --grep-invert '@slow'",
  69. "test:critical-smoke": "playwright test --grep '@smoke.*@p0'"
  70. }
  71. }
  72. ```
  73. **Cypress equivalent**:
  74. ```javascript
  75. // cypress/e2e/checkout.cy.ts
  76. describe('Checkout Flow', { tags: ['@checkout'] }, () => {
  77. it('should complete purchase', { tags: ['@smoke', '@p0'] }, () => {
  78. cy.visit('/checkout');
  79. cy.get('[data-cy="card-number"]').type('4242424242424242');
  80. cy.get('[data-cy="submit-payment"]').click();
  81. cy.get('[data-cy="order-confirmation"]').should('be.visible');
  82. });
  83. it('should handle decline', { tags: ['@regression', '@p0'] }, () => {
  84. cy.visit('/checkout');
  85. cy.get('[data-cy="card-number"]').type('4000000000000002');
  86. cy.get('[data-cy="submit-payment"]').click();
  87. cy.get('[data-cy="payment-error"]').should('be.visible');
  88. });
  89. });
  90. // cypress.config.ts
  91. export default defineConfig({
  92. e2e: {
  93. env: {
  94. grepTags: process.env.GREP_TAGS || '',
  95. grepFilterSpecs: true,
  96. },
  97. setupNodeEvents(on, config) {
  98. require('@cypress/grep/src/plugin')(config);
  99. return config;
  100. },
  101. },
  102. });
  103. ```
  104. **Usage**:
  105. ```bash
  106. # Playwright
  107. npm run test:smoke # Run all @smoke tests
  108. npm run test:p0 # Run all P0 tests
  109. npm run test -- --grep "@smoke.*@p0" # Run tests with BOTH tags
  110. # Cypress (with @cypress/grep plugin)
  111. npx cypress run --env grepTags="@smoke"
  112. npx cypress run --env grepTags="@p0+@smoke" # AND logic
  113. npx cypress run --env grepTags="@p0 @p1" # OR logic
  114. ```
  115. **Key Points**:
  116. - **Multiple tags per test**: Combine priority (@p0) with stage (@smoke)
  117. - **AND/OR logic**: Grep supports complex filtering
  118. - **Clear naming**: Tags document test importance
  119. - **Fast feedback**: @smoke runs < 5 min, full suite < 30 min
  120. - **CI integration**: Different jobs run different tag combinations
  121. ---
  122. ### Example 2: Spec Filter Pattern (File-Based Selection)
  123. **Context**: Run tests by file path pattern or directory for targeted execution.
  124. **Implementation**:
  125. ```bash
  126. #!/bin/bash
  127. # scripts/selective-spec-runner.sh
  128. # Run tests based on spec file patterns
  129. set -e
  130. PATTERN=${1:-"**/*.spec.ts"}
  131. TEST_ENV=${TEST_ENV:-local}
  132. echo "🎯 Selective Spec Runner"
  133. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  134. echo "Pattern: $PATTERN"
  135. echo "Environment: $TEST_ENV"
  136. echo ""
  137. # Pattern examples and their use cases
  138. case "$PATTERN" in
  139. "**/checkout*")
  140. echo "📦 Running checkout-related tests"
  141. npx playwright test --grep-files="**/checkout*"
  142. ;;
  143. "**/auth*"|"**/login*"|"**/signup*")
  144. echo "🔐 Running authentication tests"
  145. npx playwright test --grep-files="**/auth*|**/login*|**/signup*"
  146. ;;
  147. "tests/e2e/**")
  148. echo "🌐 Running all E2E tests"
  149. npx playwright test tests/e2e/
  150. ;;
  151. "tests/integration/**")
  152. echo "🔌 Running all integration tests"
  153. npx playwright test tests/integration/
  154. ;;
  155. "tests/component/**")
  156. echo "🧩 Running all component tests"
  157. npx playwright test tests/component/
  158. ;;
  159. *)
  160. echo "🔍 Running tests matching pattern: $PATTERN"
  161. npx playwright test "$PATTERN"
  162. ;;
  163. esac
  164. ```
  165. **Playwright config for file filtering**:
  166. ```typescript
  167. // playwright.config.ts
  168. import { defineConfig, devices } from '@playwright/test';
  169. export default defineConfig({
  170. // ... other config
  171. // Project-based organization
  172. projects: [
  173. {
  174. name: 'smoke',
  175. testMatch: /.*smoke.*\.spec\.ts/,
  176. retries: 0,
  177. },
  178. {
  179. name: 'e2e',
  180. testMatch: /tests\/e2e\/.*\.spec\.ts/,
  181. retries: 2,
  182. },
  183. {
  184. name: 'integration',
  185. testMatch: /tests\/integration\/.*\.spec\.ts/,
  186. retries: 1,
  187. },
  188. {
  189. name: 'component',
  190. testMatch: /tests\/component\/.*\.spec\.ts/,
  191. use: { ...devices['Desktop Chrome'] },
  192. },
  193. ],
  194. });
  195. ```
  196. **Advanced pattern matching**:
  197. ```typescript
  198. // scripts/run-by-component.ts
  199. /**
  200. * Run tests related to specific component(s)
  201. * Usage: npm run test:component UserProfile,Settings
  202. */
  203. import { execSync } from 'child_process';
  204. const components = process.argv[2]?.split(',') || [];
  205. if (components.length === 0) {
  206. console.error('❌ No components specified');
  207. console.log('Usage: npm run test:component UserProfile,Settings');
  208. process.exit(1);
  209. }
  210. // Convert component names to glob patterns
  211. const patterns = components.map((comp) => `**/*${comp}*.spec.ts`).join(' ');
  212. console.log(`🧩 Running tests for components: ${components.join(', ')}`);
  213. console.log(`Patterns: ${patterns}`);
  214. try {
  215. execSync(`npx playwright test ${patterns}`, {
  216. stdio: 'inherit',
  217. env: { ...process.env, CI: 'false' },
  218. });
  219. } catch (error) {
  220. process.exit(1);
  221. }
  222. ```
  223. **package.json scripts**:
  224. ```json
  225. {
  226. "scripts": {
  227. "test:checkout": "playwright test **/checkout*.spec.ts",
  228. "test:auth": "playwright test **/auth*.spec.ts **/login*.spec.ts",
  229. "test:e2e": "playwright test tests/e2e/",
  230. "test:integration": "playwright test tests/integration/",
  231. "test:component": "ts-node scripts/run-by-component.ts",
  232. "test:project": "playwright test --project",
  233. "test:smoke-project": "playwright test --project smoke"
  234. }
  235. }
  236. ```
  237. **Key Points**:
  238. - **Glob patterns**: Wildcards match file paths flexibly
  239. - **Project isolation**: Separate projects have different configs
  240. - **Component targeting**: Run tests for specific features
  241. - **Directory-based**: Organize tests by type (e2e, integration, component)
  242. - **CI optimization**: Run subsets in parallel CI jobs
  243. ---
  244. ### Example 3: Diff-Based Test Selection (Changed Files Only)
  245. **Context**: Run only tests affected by code changes for maximum speed.
  246. **Implementation**:
  247. ```bash
  248. #!/bin/bash
  249. # scripts/test-changed-files.sh
  250. # Intelligent test selection based on git diff
  251. set -e
  252. BASE_BRANCH=${BASE_BRANCH:-main}
  253. TEST_ENV=${TEST_ENV:-local}
  254. echo "🔍 Changed File Test Selector"
  255. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  256. echo "Base branch: $BASE_BRANCH"
  257. echo "Environment: $TEST_ENV"
  258. echo ""
  259. # Get changed files
  260. CHANGED_FILES=$(git diff --name-only $BASE_BRANCH...HEAD)
  261. if [ -z "$CHANGED_FILES" ]; then
  262. echo "✅ No files changed. Skipping tests."
  263. exit 0
  264. fi
  265. echo "Changed files:"
  266. echo "$CHANGED_FILES" | sed 's/^/ - /'
  267. echo ""
  268. # Arrays to collect test specs
  269. DIRECT_TEST_FILES=()
  270. RELATED_TEST_FILES=()
  271. RUN_ALL_TESTS=false
  272. # Process each changed file
  273. while IFS= read -r file; do
  274. case "$file" in
  275. # Changed test files: run them directly
  276. *.spec.ts|*.spec.js|*.test.ts|*.test.js|*.cy.ts|*.cy.js)
  277. DIRECT_TEST_FILES+=("$file")
  278. ;;
  279. # Critical config changes: run ALL tests
  280. package.json|package-lock.json|playwright.config.ts|cypress.config.ts|tsconfig.json|.github/workflows/*)
  281. echo "⚠️ Critical file changed: $file"
  282. RUN_ALL_TESTS=true
  283. break
  284. ;;
  285. # Component changes: find related tests
  286. src/components/*.tsx|src/components/*.jsx)
  287. COMPONENT_NAME=$(basename "$file" | sed 's/\.[^.]*$//')
  288. echo "🧩 Component changed: $COMPONENT_NAME"
  289. # Find tests matching component name
  290. FOUND_TESTS=$(find tests -name "*${COMPONENT_NAME}*.spec.ts" -o -name "*${COMPONENT_NAME}*.cy.ts" 2>/dev/null || true)
  291. if [ -n "$FOUND_TESTS" ]; then
  292. while IFS= read -r test_file; do
  293. RELATED_TEST_FILES+=("$test_file")
  294. done <<< "$FOUND_TESTS"
  295. fi
  296. ;;
  297. # Utility/lib changes: run integration + unit tests
  298. src/utils/*|src/lib/*|src/helpers/*)
  299. echo "⚙️ Utility file changed: $file"
  300. RELATED_TEST_FILES+=($(find tests/unit tests/integration -name "*.spec.ts" 2>/dev/null || true))
  301. ;;
  302. # API changes: run integration + e2e tests
  303. src/api/*|src/services/*|src/controllers/*)
  304. echo "🔌 API file changed: $file"
  305. RELATED_TEST_FILES+=($(find tests/integration tests/e2e -name "*.spec.ts" 2>/dev/null || true))
  306. ;;
  307. # Type changes: run all TypeScript tests
  308. *.d.ts|src/types/*)
  309. echo "📝 Type definition changed: $file"
  310. RUN_ALL_TESTS=true
  311. break
  312. ;;
  313. # Documentation only: skip tests
  314. *.md|docs/*|README*)
  315. echo "📄 Documentation changed: $file (no tests needed)"
  316. ;;
  317. *)
  318. echo "❓ Unclassified change: $file (running smoke tests)"
  319. RELATED_TEST_FILES+=($(find tests -name "*smoke*.spec.ts" 2>/dev/null || true))
  320. ;;
  321. esac
  322. done <<< "$CHANGED_FILES"
  323. # Execute tests based on analysis
  324. if [ "$RUN_ALL_TESTS" = true ]; then
  325. echo ""
  326. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  327. echo "🚨 Running FULL test suite (critical changes detected)"
  328. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  329. npm run test
  330. exit $?
  331. fi
  332. # Combine and deduplicate test files
  333. ALL_TEST_FILES=(${DIRECT_TEST_FILES[@]} ${RELATED_TEST_FILES[@]})
  334. UNIQUE_TEST_FILES=($(echo "${ALL_TEST_FILES[@]}" | tr ' ' '\n' | sort -u))
  335. if [ ${#UNIQUE_TEST_FILES[@]} -eq 0 ]; then
  336. echo ""
  337. echo "✅ No tests found for changed files. Running smoke tests."
  338. npm run test:smoke
  339. exit $?
  340. fi
  341. echo ""
  342. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  343. echo "🎯 Running ${#UNIQUE_TEST_FILES[@]} test file(s)"
  344. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  345. for test_file in "${UNIQUE_TEST_FILES[@]}"; do
  346. echo " - $test_file"
  347. done
  348. echo ""
  349. npm run test -- "${UNIQUE_TEST_FILES[@]}"
  350. ```
  351. **GitHub Actions integration**:
  352. ```yaml
  353. # .github/workflows/test-changed.yml
  354. name: Test Changed Files
  355. on:
  356. pull_request:
  357. types: [opened, synchronize, reopened]
  358. jobs:
  359. detect-and-test:
  360. runs-on: ubuntu-latest
  361. steps:
  362. - uses: actions/checkout@v4
  363. with:
  364. fetch-depth: 0 # Full history for accurate diff
  365. - name: Get changed files
  366. id: changed-files
  367. uses: tj-actions/changed-files@v40
  368. with:
  369. files: |
  370. src/**
  371. tests/**
  372. *.config.ts
  373. files_ignore: |
  374. **/*.md
  375. docs/**
  376. - name: Run tests for changed files
  377. if: steps.changed-files.outputs.any_changed == 'true'
  378. run: |
  379. echo "Changed files: ${{ steps.changed-files.outputs.all_changed_files }}"
  380. bash scripts/test-changed-files.sh
  381. env:
  382. BASE_BRANCH: ${{ github.base_ref }}
  383. TEST_ENV: staging
  384. ```
  385. **Key Points**:
  386. - **Intelligent mapping**: Code changes → related tests
  387. - **Critical file detection**: Config changes = full suite
  388. - **Component mapping**: UI changes → component + E2E tests
  389. - **Fast feedback**: Run only what's needed (< 2 min typical)
  390. - **Safety net**: Unrecognized changes run smoke tests
  391. ---
  392. ### Example 4: Promotion Rules (Pre-Commit → CI → Staging → Production)
  393. **Context**: Progressive test execution strategy across deployment stages.
  394. **Implementation**:
  395. ```typescript
  396. // scripts/test-promotion-strategy.ts
  397. /**
  398. * Test Promotion Strategy
  399. * Defines which tests run at each stage of the development lifecycle
  400. */
  401. export type TestStage = 'pre-commit' | 'ci-pr' | 'ci-merge' | 'staging' | 'production';
  402. export type TestPromotion = {
  403. stage: TestStage;
  404. description: string;
  405. testCommand: string;
  406. timebudget: string; // minutes
  407. required: boolean;
  408. failureAction: 'block' | 'warn' | 'alert';
  409. };
  410. export const TEST_PROMOTION_RULES: Record<TestStage, TestPromotion> = {
  411. 'pre-commit': {
  412. stage: 'pre-commit',
  413. description: 'Local developer checks before git commit',
  414. testCommand: 'npm run test:smoke',
  415. timebudget: '2',
  416. required: true,
  417. failureAction: 'block',
  418. },
  419. 'ci-pr': {
  420. stage: 'ci-pr',
  421. description: 'CI checks on pull request creation/update',
  422. testCommand: 'npm run test:changed && npm run test:p0-p1',
  423. timebudget: '10',
  424. required: true,
  425. failureAction: 'block',
  426. },
  427. 'ci-merge': {
  428. stage: 'ci-merge',
  429. description: 'Full regression before merge to main',
  430. testCommand: 'npm run test:regression',
  431. timebudget: '30',
  432. required: true,
  433. failureAction: 'block',
  434. },
  435. staging: {
  436. stage: 'staging',
  437. description: 'Post-deployment validation in staging environment',
  438. testCommand: 'npm run test:e2e -- --grep "@smoke"',
  439. timebudget: '15',
  440. required: true,
  441. failureAction: 'block',
  442. },
  443. production: {
  444. stage: 'production',
  445. description: 'Production smoke tests post-deployment',
  446. testCommand: 'npm run test:e2e:prod -- --grep "@smoke.*@p0"',
  447. timebudget: '5',
  448. required: false,
  449. failureAction: 'alert',
  450. },
  451. };
  452. /**
  453. * Get tests to run for a specific stage
  454. */
  455. export function getTestsForStage(stage: TestStage): TestPromotion {
  456. return TEST_PROMOTION_RULES[stage];
  457. }
  458. /**
  459. * Validate if tests can be promoted to next stage
  460. */
  461. export function canPromote(currentStage: TestStage, testsPassed: boolean): boolean {
  462. const promotion = TEST_PROMOTION_RULES[currentStage];
  463. if (!promotion.required) {
  464. return true; // Non-required tests don't block promotion
  465. }
  466. return testsPassed;
  467. }
  468. ```
  469. **Husky pre-commit hook**:
  470. ```bash
  471. #!/bin/bash
  472. # .husky/pre-commit
  473. # Run smoke tests before allowing commit
  474. echo "🔍 Running pre-commit tests..."
  475. npm run test:smoke
  476. if [ $? -ne 0 ]; then
  477. echo ""
  478. echo "❌ Pre-commit tests failed!"
  479. echo "Please fix failures before committing."
  480. echo ""
  481. echo "To skip (NOT recommended): git commit --no-verify"
  482. exit 1
  483. fi
  484. echo "✅ Pre-commit tests passed"
  485. ```
  486. **GitHub Actions workflow**:
  487. ```yaml
  488. # .github/workflows/test-promotion.yml
  489. name: Test Promotion Strategy
  490. on:
  491. pull_request:
  492. push:
  493. branches: [main]
  494. workflow_dispatch:
  495. jobs:
  496. # Stage 1: PR tests (changed + P0-P1)
  497. pr-tests:
  498. if: github.event_name == 'pull_request'
  499. runs-on: ubuntu-latest
  500. timeout-minutes: 10
  501. steps:
  502. - uses: actions/checkout@v4
  503. - name: Run PR-level tests
  504. run: |
  505. npm run test:changed
  506. npm run test:p0-p1
  507. # Stage 2: Full regression (pre-merge)
  508. regression-tests:
  509. if: github.event_name == 'push' && github.ref == 'refs/heads/main'
  510. runs-on: ubuntu-latest
  511. timeout-minutes: 30
  512. steps:
  513. - uses: actions/checkout@v4
  514. - name: Run full regression
  515. run: npm run test:regression
  516. # Stage 3: Staging validation (post-deploy)
  517. staging-smoke:
  518. if: github.event_name == 'workflow_dispatch'
  519. runs-on: ubuntu-latest
  520. timeout-minutes: 15
  521. steps:
  522. - uses: actions/checkout@v4
  523. - name: Run staging smoke tests
  524. run: npm run test:e2e -- --grep "@smoke"
  525. env:
  526. TEST_ENV: staging
  527. # Stage 4: Production smoke (post-deploy, non-blocking)
  528. production-smoke:
  529. if: github.event_name == 'workflow_dispatch'
  530. runs-on: ubuntu-latest
  531. timeout-minutes: 5
  532. continue-on-error: true # Don't fail deployment if smoke tests fail
  533. steps:
  534. - uses: actions/checkout@v4
  535. - name: Run production smoke tests
  536. run: npm run test:e2e:prod -- --grep "@smoke.*@p0"
  537. env:
  538. TEST_ENV: production
  539. - name: Alert on failure
  540. if: failure()
  541. uses: 8398a7/action-slack@v3
  542. with:
  543. status: ${{ job.status }}
  544. text: '🚨 Production smoke tests failed!'
  545. webhook_url: ${{ secrets.SLACK_WEBHOOK }}
  546. ```
  547. **Selection strategy documentation**:
  548. ````markdown
  549. # Test Selection Strategy
  550. ## Test Promotion Stages
  551. | Stage | Tests Run | Time Budget | Blocks Deploy | Failure Action |
  552. | ---------- | ------------------- | ----------- | ------------- | -------------- |
  553. | Pre-Commit | Smoke (@smoke) | 2 min | ✅ Yes | Block commit |
  554. | CI PR | Changed + P0-P1 | 10 min | ✅ Yes | Block merge |
  555. | CI Merge | Full regression | 30 min | ✅ Yes | Block deploy |
  556. | Staging | E2E smoke | 15 min | ✅ Yes | Rollback |
  557. | Production | Critical smoke only | 5 min | ❌ No | Alert team |
  558. ## When Full Regression Runs
  559. Full regression suite (`npm run test:regression`) runs in these scenarios:
  560. - ✅ Before merging to `main` (CI Merge stage)
  561. - ✅ Nightly builds (scheduled workflow)
  562. - ✅ Manual trigger (workflow_dispatch)
  563. - ✅ Release candidate testing
  564. Full regression does NOT run on:
  565. - ❌ Every PR commit (too slow)
  566. - ❌ Pre-commit hooks (too slow)
  567. - ❌ Production deployments (deploy-blocking)
  568. ## Override Scenarios
  569. Skip tests (emergency only):
  570. ```bash
  571. git commit --no-verify # Skip pre-commit hook
  572. gh pr merge --admin # Force merge (requires admin)
  573. ```
  574. ````
  575. ```
  576. **Key Points**:
  577. - **Progressive validation**: More tests at each stage
  578. - **Time budgets**: Clear expectations per stage
  579. - **Blocking vs. alerting**: Production tests don't block deploy
  580. - **Documentation**: Team knows when full regression runs
  581. - **Emergency overrides**: Documented but discouraged
  582. ---
  583. ## Test Selection Strategy Checklist
  584. Before implementing selective testing, verify:
  585. - [ ] **Tag strategy defined**: @smoke, @p0-p3, @regression documented
  586. - [ ] **Time budgets set**: Each stage has clear timeout (smoke < 5 min, full < 30 min)
  587. - [ ] **Changed file mapping**: Code changes → test selection logic implemented
  588. - [ ] **Promotion rules documented**: README explains when full regression runs
  589. - [ ] **CI integration**: GitHub Actions uses selective strategy
  590. - [ ] **Local parity**: Developers can run same selections locally
  591. - [ ] **Emergency overrides**: Skip mechanisms documented (--no-verify, admin merge)
  592. - [ ] **Metrics tracked**: Monitor test execution time and selection accuracy
  593. ## Integration Points
  594. - Used in workflows: `*ci` (CI/CD setup), `*automate` (test generation with tags)
  595. - Related fragments: `ci-burn-in.md`, `test-priorities-matrix.md`, `test-quality.md`
  596. - Selection tools: Playwright --grep, Cypress @cypress/grep, git diff
  597. _Source: 32+ selective testing strategies blog, Murat testing philosophy, enterprise CI optimization_
  598. ```