# TC Generation Strategy: 3-Layer Model for Feature Testing

**Version:** 1.0 | **Owner:** QA Leadership | **Framework:** ISTQB + Agile + Lean

**Used by:**
- **`/tas-functest` Step 3 (AI):** Read before routing TPs → technique + TC expansion. Apply 3-layer model, dedup rules, Autoable classification, Depends propagation, and Test Data rules.
- **Phase 2 (PE — Human QC):** Read before designing test cases in Func-Test §4. Apply the 3-layer model manually to map TPs → TCs and validate dependency chains.
- **Phase 3 (AI — Automation):** Read before converting Phase 2 Human Steps → Locator DSL. Apply Layer 2 State Model and dependency propagation to generate correct `Depends:` chains.

**Input requires:** TestChecklist §1.1, §1.4, §2.1, §2.3 available before applying. Layer 2/3 expansion requires Step 3 routing to be complete first.

---

## The 3-Layer Model

### Layer 1: Business Flows → TC Types

**Input:** TestChecklist §1.1 (In-Scope Business Flows with Risk Tags, Priorities, Dependencies)

**Decision Rule:**
- **Main flow (P0):** 1 Happy Path TC minimum
- **Alt flow (P1):** 1 Variant TC (different path, role, or mechanism)
- **Edge flow (P2):** 1 Boundary TC + 1 Error TC

**Example:**
```
§1.1 Flow: "User logs in with valid email/password" (Main, P0, Depends: None)
   → FuncTest §4: F001_AC1_BL_001 (Happy Path: valid credentials)

§1.1 Flow: "Login fails due to invalid password" (Edge, P2, Depends: F001-AC1-Main)
   → FuncTest §4: F001_AC1_VAL_001 (Negative: empty password) + F001_AC1_EXC_001 (Exception: max attempts)

§1.1 Flow: "Login via SSO / SAML" (Alt, P1, Depends: None)
   → FuncTest §4: F001_AC2_BL_001 (Variant: SSO flow, same AC or different)
```

**Benefit:** Every business requirement maps to at least one TC. No orphaned flows.

---

### Layer 2: UI/UX Test Basis → State Model

**Input:** TestChecklist §1.4 (Screens, Preconditions, Fields, Actions, States)

**Decision Rule:**
- **Screen Precondition (§1.4)** → TC Pre-Condition `State:` field
  - Example: "User authenticated, cart ≥1 item, session active"
  - All TCs for this screen inherit this state requirement
  - *Dedup rule:* Same screen + same precondition + **same assertion point** = shared state; don't create new TC
  - *Complementary rule:* Different assertion points on same screen = KEEP BOTH (e.g., DOM structure ≠ text language)

- **Field Constraints & Actions (§1.4)** → TC Data + Human Steps
  - Example: Email field "(required, max 255, RFC 5322)" → Apply BVA (length) + EP (format)
  - Different constraints = different TCs; same constraint = one TC

- **State Transitions (§1.4 States)** → Expected Result Assertions
  - Example: "After submit, show success toast + redirect to /dashboard"
  - One state transition = one TC with multiple assertions (not 3 separate TCs)

- **TP Depends On (§2.1)** → TC Pre-Condition `Depends:` field
  - TP `Depends On` = None → no `Depends:` (entry-point TC). Otherwise → resolve the source TP's primary TC and propagate.
  - Full propagation algorithm (primary-TC selection, all-categories rule, universal propagation to BVA/EP variants) and cycle/missing/self-dep validation are defined once in § Dependency Management below. Do not restate the rules here — apply that section.

**Example:**
```
§1.4 Screen: "Checkout Form"
  Precondition: User authenticated, cart ≥1 item, session active
  Fields: Amount (0.01–999.99), Card (16 digits), Expiry (MM/YY)
  States: success (redirect), error (toast), loading (button disabled)

§4 Pre-Conditions for all checkout TCs:
  Page: Checkout (/checkout) | Role: Standard | State: user="auth" cart_count≥1 session_active=true

§4 Expected Results vary by scenario:
  TC1 (valid payment): • show toast "Order confirmed" + • redirect to /confirmation
  TC2 (insufficient funds): • show error toast "Payment failed" + • stay at /checkout
  TC3 (network timeout): • show error "Connection timeout" + • restore submit button after retry
  → 3 TCs, NOT 6 (because state transitions are part of one TC, not separate TCs)
```

**Benefit:** Test data and state requirements are explicit, not hidden. Reduces integration bugs where TC runs in wrong environment.

---

### Layer 3: Test Techniques → TC Count Expansion

**Input:** TestChecklist §2.1 (Test Points + Categories) + §2.3 (Test Data & Input Constraints)

> **Technique per category** (BVA, EP, Decision Table, …) is owned by [test-design.md § TP Category → Test Approach Mapping](test-design.md). This table owns only the **TC-count expansion** and **dedup** consequences of applying that technique — it does not redefine which technique to use.

**TC-Count + Dedup Rule per Category** (trigger → how many TCs the technique produces):

| Category | Trigger | TC Count | Dedup Rule |
|---|---|---|---|
| **VAL** | Field: min/max (BVA) | 4: min-1, min, max, max+1 | If 2 ACs validate same field → 1 TC |
| **VAL** | Field: valid/invalid (EP) | 1 valid + 1 invalid | If 2 ACs, same field → 1 TC (merge) |
| **BL** | 2+ conditions (Decision Table) | 1 per outcome row | If 2 flows same logic → 1 TC. If 4+ independent conditions → use pairwise grouping (collapse by shared outcome) before counting |
| **BL** | Workflow: step 1→2→3 (State Transition) | 1 TC with Depends chain | NOT 3 TCs; use Depends, not merge |
| **GUI** | Layout/visual | 1 per TP | Manual-only; no dedup (each screen once) |
| **EXC** | Error message (Negative) | 1 per error type | If 2 flows same error → 1 TC |
| **SEC/INT** | Observed in browser | 1 per TP | Route non-observable to /tas-security |

#### Inclusion Rules (FuncTest Frontmatter) — CANONICAL ⭐

> **Single source of truth** for `test_strategy.inclusion_rules` in FuncTest frontmatter. Template provides defaults; `/tas-functest` Step 5 carries from TestChecklist routing if specified.

**Default inclusion rules per category (FuncTest scope = manual GUI execution):**

| Category | Include | Autoable | Rationale |
|---|---|---|---|
| **GUI** | `true` | `false` | Visual/layout requires human eyes |
| **VAL** | `true` | `true` | Input/output assertions automatable |
| **BL** | `true` | `true` | State changes verifiable via API/DOM |
| **EXC** | `true` | `true` | Error messages/states automatable |
| **SEC** | `ui_observable_only` | `conditional` | Auth/permission UI-observable; non-UI → `/tas-security` |
| **INT** | `ui_observable_only` | `true` | Integration results visible in UI; API-only → `/tas-apitest` |
| **PERF** | `false` | `false` | Performance testing → NFR pipeline (Lighthouse/k6) |

**Rule:** If TestChecklist routing specifies different inclusion rules (e.g., PERF in-scope for load-critical feature), override defaults.

#### Cross-Cutting TP Inclusion Rules (Extension)

| TP Type | Priority | Category | Include | Rationale |
|---------|----------|----------|---------|-----------|
| cross_cutting | P0, P1 | GUI, VAL, BL, EXC, SEC | `true` | UI-observable functional scope |
| cross_cutting | P0, P1 | PERF, INT (API-only) | `false` | Route to `/tas-apitest-plan` (see verification-layers.md §4) |
| cross_cutting | P2 | GUI, UX | `conditional` | Optional → §5 Inherited Risk (discretionary coverage) |

**Used by:** `/tas-functest` Step 3 (filter cross-cutting TPs before TC expansion).

---

### Autoable Classification (Phase 2 → Phase 3 Handoff) — CANONICAL ⭐

> **Single source of truth** for the `Autoable` column. Allowed values: **`YES` | `NO`** (binary, no MAYBE). `/tas-functest` Step 3 references this — do not use a different vocabulary (e.g. `Yes`/`None`/`true`) anywhere.

**Populate in Func-Test §4 `Autoable` column per TC Category:**

| Category | Autoable | Rationale | Route To |
|---|---|---|---|
| **VAL** | YES | Input + output assertions automatable; no visual dependencies | Form validation, error messages, range checks |
| **BL** | YES | State changes, business logic outcomes verifiable via API/DOM | Order status, payment result, workflow progression |
| **EXC** | YES | Error message text/code, error states automatable | "Field required", HTTP 400, timeout handling |
| **GUI** | NO | Visual/layout/alignment/responsive verification requires human eyes | Header position, button size, text readability, spacing |
| **SEC** | NO | Auth/perm/XSS = API/security layer (out of functest scope) | `/tas-apitest-plan` (auth) or `/tas-security` (XSS) |
| **INT** | NO | API mocking/integration = API layer (out of functest scope) | `/tas-apitest-plan` |
| **ROB** | NO | Network throttle/session timeout = NFR layer (out of functest scope) | NFR pipeline |

**Scope boundary:** See [verification-layers.md §4 Scope-Conflict Matrix](../../rules/qa/verification-layers.md#4-scope-conflict-matrix-functest--apitest-plan). Functest owns UI/observable black-box only (ISTQB Ch 4.2).

**Rule:** If TC spans multiple categories, use **highest** automation readiness:
- VAL + BL = YES (both UI-observable functional)
- BL + SEC = NO (SEC blocks functest scope → route to `/tas-apitest-plan`)
- GUI + any = NO (GUI requires human eyes)

**Phase 3 Handoff:** `Route To` column carries the routing. Scope boundary: [verification-layers.md §4](../../rules/qa/verification-layers.md#4-scope-conflict-matrix-functest--apitest-plan).

**Example:**
```
§2.1 TP: F001-VAL-001 "Amount field: 0.01–999.99"
§2.3 Data: amount | Numeric | 0.01–999.99 | Test data: 0.01 / -1 / 999.99 / 1000

Layer 3 Application:
  Trigger: min/max pattern (0.01 to 999.99) detected
  Technique: BVA → 4 TCs

  F001_AC1_VAL_001a: amount="0.00" [BVA·min-1]      → Error "Minimum is $0.01"
  F001_AC1_VAL_001b: amount="0.01" [BVA·min]        → Success ✅
  F001_AC1_VAL_001c: amount="999.99" [BVA·max]      → Success ✅
  F001_AC1_VAL_001d: amount="1000.00" [BVA·max+1]   → Error "Maximum is $999.99"
  
  RESULT: 1 TP expands to 4 TCs (expected expansion, not duplication)
```

**Benefit:** Technique application is systematic and traceable. TCs generated, not guessed.

---

## The 6 Deduplication Rules

**Decision Criterion (ISTQB):** Two TCs are duplicates if and only if they share the **same assertion point** — the specific behavior being verified, independent of input values. If assertion points differ, TCs are complementary, not duplicates.

| Merge (Duplicate) | Keep Both (Complementary) |
|---|---|
| Same field + same technique + same expected outcome | Same field + different expected outcome (BVA·min vs BVA·min-1 expose different failure modes) |
| Same business flow, different ACs | Same screen + different property being verified (DOM order ≠ text language) |
| Same component, same state assertion | Same TP, different variants (Dutch /nl/ vs English /en/ = different assertion outputs) |

**When do you have a duplicate TC (and should merge)?** Apply the decision criterion above to Rules 1–6 below.

### Rule 1: Same TP + Same Category + Same Test Logic = ONE TC

```
Duplicate Scenario:
  F001-VAL-001 (Email validation — RFC 5322) required for AC-1 AND AC-2

Decision: ONE TC (F001_AC1_VAL_001) covers both ACs
  Section 3 Coverage links:
    AC-1 → [F001_AC1_VAL_001]
    AC-2 → [F001_AC1_VAL_001]
    
Rationale: Validating email format in AC-2 doesn't require re-running same TC. 
One assertion point. Running twice = zero additional coverage.
```

### Rule 2: Different ACs, Identical Business Behavior = MERGE to Primary AC

```
Duplicate Scenario:
  AC-1 "User logs in with valid credentials" (Main)
  AC-2 "After signup, user must log in" (Alt)
  Both flows require same "valid login" behavior

Decision: ONE TC (F001_AC1_BL_001) covers both ACs
  (Not: F001_AC1_BL_001 + F001_AC2_BL_001 as duplicates)
  
Assign to Primary AC: AC-1 (direct login ownership)
Link in Section 3: AC-1 & AC-2 both covered by F001_AC1_BL_001

Rationale: Same assertion point. Second run ≠ additional coverage.
Example: "User redirected to dashboard" happens once per login, not twice per AC.
```

### Rule 3: Same Field, Different Techniques = SEPARATE TCs

```
Complementary Scenario (NOT duplicate):
  Email field has TWO constraints:
    1. Length: 8–255 chars → BVA technique
    2. Format: RFC 5322 → EP technique

Decision: TWO TCs (not a merge)
  F001_AC1_VAL_001a: "email" [BVA·min-1]     → "field must be ≥8 chars"
  F001_AC1_VAL_001b: "email" [BVA·max+1]     → "field must be ≤255 chars"
  F001_AC1_VAL_002: "invalid@" [EP·invalid]  → "invalid email format"
  
Rationale: Different techniques expose different failure modes.
These are COMPLEMENTARY, not duplicates.
```

**Sub-rule: Same technique, different test data values**

When the same technique is applied with different values:
- **Different failure modes** (BVA·min-1 vs BVA·max+1 = different error states) → SEPARATE TCs
- **Same outcome** (EP·valid-email-A and EP·valid-email-B both succeed) → ONE TC with multi-row data (from §2.3)

Example:
```
Email field, EP technique, Valid values: ["test@example.com", "user+tag@domain.co.uk"]
Both values satisfy "valid RFC 5322 format" (same assertion point, same outcome)
→ 1 TC with data: ["test@example.com", "user+tag@domain.co.uk"] (NOT 2 separate TCs)

Age field, BVA technique, Boundary values: 18, 100, 17, 101
Different assertions (valid, valid, invalid-too-young, invalid-too-old)
→ 4 separate TCs, each with one value (different assertion points)
```

### Rule 4: Multi-Step Workflow = ONE TC with Depends, NOT Multiple TCs

```
Bad Pattern (Over-coverage):
  "Create order" (TC1) → "Confirm payment" (TC2) → "Send receipt" (TC3)
  Running all 3 means: 3 separate pre-conditions, 3 separate data setups, 3x teardown overhead
  
Good Pattern (Lean coverage):
  F001_AC3_BL_001: "Complete checkout flow (order → payment → receipt)"
    Steps: 1. Add item 2. Proceed to checkout 3. Enter payment 4. Confirm
    — Verify: order created
    — Verify: payment processed
    — Verify: receipt sent
    — Teardown: Delete order
  
  WITH Depends chains for state-dependent sub-steps:
    F001_AC3_BL_001a (Create order) → No depends
    F001_AC3_BL_001b (Payment) → Depends: F001_AC3_BL_001a
    F001_AC3_BL_001c (Receipt) → Depends: F001_AC3_BL_001b
  
Rationale: One workflow = one user journey = one TC.
Depends field handles execution order. Not 3x the maintenance burden.
```

### Rule 5: Shared Components = Inherit Parent TC, Add Variant Only

```
Shared Component Scenario (§1.4):
  LoginForm used by Feature-A (authentication) AND Feature-B (admin panel)
  
Good Pattern:
  Feature-A: F001_AC1_BL_001 "Valid login redirects to dashboard" → CREATED, OWNED
  Feature-B: References F001_AC1_BL_001 (shared TC)
             + F002_AC1_BL_001 "After admin login, admin panel loads" (feature-specific state diff)
  
NOT: Feature-B: Creates duplicate F002_AC1_BL_001 testing same login behavior

Rationale: LoginForm code is shared → testing it twice = redundant.
Create variant TCs only if post-login state differs (e.g., admin vs user dashboard).
```

### Rule 6: Test Data Sourced from TestChecklist §2.3, NOT TP Definition

```
Data Propagation Scenario (CRITICAL):
  TP Definition (§2.1): "Complete Card 1: select location (checkbox), symptoms (checkbox), duration (radio)"
  Test Data Catalog (§2.3): Location: [Face, Hands, Neck] | Symptoms: [Itching, Redness, Dryness] | Duration: > 6 weeks
  
❌ BAD: TC Data = "data: locations, symptoms, duration" (abstract TP field names)
✅ GOOD: TC Data = "locations: "[Face, Hands, Neck]" [BVA·normal] | symptoms: "[Itching, Redness, Dryness]" [BVA·normal] | duration: "> 6 weeks" [EP·valid]" (concrete values from §2.3)

Rationale: 
  - TP definition is abstract (test intention), not concrete (executable test values)
  - §2.3 test data catalog contains actual values for each field + constraints
  - QA cannot execute TC with placeholder values → must expand during generation
  - Expansion algorithm: TP field name → lookup §2.3 matching field → extract concrete values → populate TC Data column

When applying this rule:
  - Map each TC field to its §2.3 source
  - Use exact values from test data catalog (filenames, ranges, valid/invalid examples)
  - Tag appropriately (BVA for boundaries, EP for valid/invalid)
  - This prevents abstract placeholder data in FuncTest output
```

---

## Dependency Management (Critical Path Testing)

**Source:** TestChecklist §2.1 `Depends On` column. Propagate to Func-Test §4 Pre-Condition `Depends:` field.

### Case 1: Simple 1-TP → 1-TC Dependency

```
TestChecklist §2.1:
  TP001 | AC-1 | GUI     | None    → F003_AC1_GUI_001
  TP002 | AC-2 | VAL     | TP001   → F003_AC2_VAL_001
  
Func-Test §4 Pre-Condition (Depends FIRST):
  F003_AC2_VAL_001: Depends: F003_AC1_GUI_001 | Page: /skin-photos | Role: Guest
  
Meaning: F003_AC2_VAL_001 depends on F003_AC1_GUI_001 (shown first = visual priority).
```

### Case 2: TP Expands to Multiple TCs (BVA/EP)

```
TestChecklist §2.1:
  TP006 | AC-2 | VAL (BVA range 0-5)  | Depends On: TP003  → Generates 4 TCs: F003_AC2_VAL_006a/b/c/d
  
Func-Test §4 Pre-Condition (Depends FIRST in all):
  **All 4 TCs get SAME Depends** (universal propagation):
  F003_AC2_VAL_006a: Depends: F003_AC1_VAL_003a | Page: ... | Role: Guest  ← FIRST
  F003_AC2_VAL_006b: Depends: F003_AC1_VAL_003a | Page: ... | Role: Guest  ← FIRST (all variants)
  F003_AC2_VAL_006c: Depends: F003_AC1_VAL_003a | Page: ... | Role: Guest  ← FIRST
  F003_AC2_VAL_006d: Depends: F003_AC1_VAL_003a | Page: ... | Role: Guest  ← FIRST
  
Meaning: All 4 boundary tests depend on same prior TC (shown first = visual priority).
RULE: If 1 TP depends on source_TP → ALL generated TCs from current TP depend on PRIMARY TC from source_TP. Depends ALWAYS FIRST position.
```

### Validation Rules — CANONICAL ⭐

> **Single source of truth** for dependency-graph validation. [test-design.md Gate 4](test-design.md) states the design-time *requirement*; these are the mechanical *checks* (`/tas-functest` Step 8 Check 3 enforces them).

- **No cycles:** A→B→C→A = ERROR. Execution would deadlock.
- **No missing deps:** TC has "Depends: X" but X not generated = ERROR. Breaks dependency chain.
- **No self-dependencies:** TC has "Depends: {itself}" = ERROR. Self-references break execution logic (Depends must point to PRIOR TC).
- **Universal propagation:** If TP has `Depends On` filled → **ALL** TCs generated from that TP must have Depends (not just primary TC).
- **Category flexibility:** Depends applies to VAL/GUI/BL/EXC/SEC/INT (all categories), not just BL. Any TC can depend on any prior TC.

**Execution:** Run dependencies in topological order. If dependency fails (FAIL/BLOCKED), skip all dependent TCs.

---

## Implementation Checklist

### For PE (Test Designer):

- [ ] Read TestChecklist §1.1 (flows), §1.4 (screens/state), §2.1 (TPs), §2.3 (test data)
- [ ] Layer 1: Map each flow to TC type (Main/Alt/Edge)
- [ ] Layer 2: Define Pre-Condition State for each unique screen + precondition combo
- [ ] Layer 3: Apply techniques per TP category; expand TCs as needed (BVA=4, EP=2, etc.)
- [ ] Dedup: Apply 6 rules; ask team if unsure (no "creative" merges)
- [ ] Depends: Check §2.1 `Depends On` column; propagate to Pre-Condition `Depends:` field
- [ ] Validation: Run Step 8 checks; resolve WARNs before approval

