Flaky Test Classification with Context-Augmented LLM Analysis
This skill enables Claude to analyze test code for flakiness — the property where a test yields inconsistent pass/fail results on the same code revision. Based on empirical findings from Berndt et al. (2026), which demonstrated that LLMs performing zero-shot, few-shot, or chain-of-thought classification on isolated test code perform only marginally better than random guessing, this skill implements a context-augmented approach: gathering project artifacts (build configs, production code under test, environment setup, CI configuration) before making a flakiness judgment. The paper's key insight is that flakiness signals live outside the test method itself, so this skill systematically retrieves that missing context.
When to Use
- When a user reports intermittent test failures and asks "is this test flaky?"
- When triaging a test suite to identify tests likely to produce non-deterministic results
- When a user pastes a test method and asks why it might be unreliable
- When reviewing a PR that adds new tests and checking for flakiness risk factors
- When a CI pipeline has inconsistent test results and the user wants to identify root causes
- When migrating or refactoring tests and assessing which ones need hardening against flakiness
Key Technique
Why test code alone fails. Berndt et al. evaluated GPT-4o, GPT-4o-mini, and CodeLlama across zero-shot, few-shot, and chain-of-thought prompting on two established datasets (IDoFT and FlakeFlagger). Even the best prompt-model combination achieved results only marginally above random chance (MCC near zero). A manual investigation of 50 samples confirmed the root cause: isolated test methods lack the contextual signals needed to determine flakiness. A Thread.sleep(1000) in a test might be harmless or catastrophic depending on what it waits for — information that only exists in production code, CI config, or runtime environment.
The six flakiness root-cause categories identified in flaky test literature — and confirmed as undetectable from test code alone — are: (1) async/timing dependencies (waits, sleeps, timeouts whose adequacy depends on external systems), (2) concurrency and shared state (tests that modify shared resources without isolation), (3) test-order dependencies (tests that assume execution order or prior state), (4) external service dependencies (network calls, databases, APIs that may be unavailable), (5) environment sensitivity (file paths, OS-specific behavior, timezone/locale), and (6) randomness and non-determinism (unseeded RNGs, hash iteration order). Detecting most of these requires seeing what the test interacts with, not just the test itself.
The context-augmented approach. Instead of classifying from test code alone (which the paper proves ineffective), this skill implements what the paper recommends as future work: a retrieval-augmented strategy that gathers production code, configuration, and environment details before reasoning about flakiness. This transforms classification from a code-only task into a project-aware analysis.
Step-by-Step Workflow
Extract the test method(s) under analysis. Read the full test file and isolate each test method, preserving class-level setup/teardown (@Before, @After, setUp, tearDown, beforeEach, etc.) and any shared fields or fixtures.
Identify the production code under test. Trace imports and method calls from the test to locate the actual classes/functions being tested. Read those source files to understand what the test exercises — this is the single most important context the paper found missing.
Gather build and dependency configuration. Read pom.xml, build.gradle, package.json, requirements.txt, or equivalent. Check for test framework versions, parallelism settings (forkCount, parallel, --workers), and timeout configurations that affect test execution.
Check CI/environment configuration. Read .github/workflows/, Jenkinsfile, .gitlab-ci.yml, docker-compose.yml, or equivalent. Identify whether tests run in parallel, in containers, with specific environment variables, or against external services.
Scan for the six flakiness root-cause categories. For each test, systematically check:
- Timing:
sleep, wait, setTimeout, Thread.sleep, polling loops, @Timeout annotations
- Concurrency: shared mutable state, static fields, singleton access, missing synchronization
- Order dependency: reliance on database state, file system artifacts, or class-level mutation without cleanup
- External services: HTTP clients, database connections, message queues, file I/O to temp directories
- Environment: hardcoded paths, locale-dependent formatting, OS-specific APIs, port binding
- Randomness:
Math.random(), Random without seed, UUID.randomUUID() in assertions, HashMap iteration
Cross-reference test signals with production context. A Thread.sleep(500) is only problematic if the operation it waits on can exceed 500ms. A database call is only flaky if the test uses a real (not mocked) connection. This step resolves ambiguities that the paper showed are impossible to resolve from test code alone.
Classify each test with confidence and rationale. Assign one of: LIKELY_FLAKY (clear root cause identified with context), POSSIBLY_FLAKY (suspicious patterns but mitigating factors present), or UNLIKELY_FLAKY (no detectable risk factors). Always state the specific root-cause category and the evidence from both test and production code.
Recommend targeted fixes. For each flaky or possibly-flaky test, provide a concrete remediation: replace sleep with polling/await, mock external services, add test isolation, seed random generators, use @TempDir instead of hardcoded paths, etc.
Concrete Examples
Example 1: Timing-dependent test in a Java project
User: "This test fails intermittently in CI. Is it flaky?"
@Test
public void testAsyncNotification() {
notificationService.sendAsync("user@example.com", "Hello");
Thread.sleep(2000);
List<Notification> sent = notificationRepository.findAll();
assertEquals(1, sent.size());
}
Approach:
- Read the test — sees
Thread.sleep(2000) and async call pattern
- Read
NotificationService.sendAsync() source — discovers it uses a thread pool with configurable queue size and sends via SMTP
- Read CI config — finds tests run with
-DforkCount=4 (parallel execution) on a shared CI runner
- Check
application-test.properties — finds it points to a real SMTP test server, not a mock
Output:
Classification: LIKELY_FLAKY
Root cause: Async/timing dependency
Confidence: High
Evidence:
- Test code: Thread.sleep(2000) used as a hard wait for async operation
- Production code: NotificationService.sendAsync() submits to thread pool, then
sends SMTP email — total latency depends on SMTP server response time
- CI config: 4 parallel forks compete for resources, increasing latency variance
- No mock: Test hits a real SMTP server, adding network non-determinism
Recommendation:
Replace Thread.sleep with Awaitility polling:
await().atMost(10, SECONDS)
.untilAsserted(() ->
assertEquals(1, notificationRepository.findAll().size()));
Consider mocking the SMTP transport in unit tests.
Example 2: Shared-state test in a Python project
User: "Some tests pass alone but fail when the full suite runs. Help me find which ones are flaky."
Approach:
- Scan test files for shared module-level state: global variables, class attributes, singleton patterns
- Read
conftest.py fixtures — check scope (session, module, function) and whether fixtures mutate shared state
- Read
pytest.ini / pyproject.toml — check for --randomly-seed, parallel execution (-n auto), test ordering plugins
- Identify tests that write to shared resources (database, files, caches) without cleanup
Output:
Found 3 tests with order-dependency risk:
1. test_user_creation (test_auth.py:45)
Classification: LIKELY_FLAKY (order dependency)
- Inserts into `users` table using module-scoped db fixture
- No teardown/rollback — subsequent tests see leftover rows
- Fix: Use function-scoped fixture with transaction rollback
2. test_cache_expiry (test_cache.py:78)
Classification: POSSIBLY_FLAKY (shared state)
- Reads from module-level `_cache = {}` dict populated by test_cache_set
- If test_cache_set runs first, passes; otherwise, KeyError
- Fix: Initialize cache in each test or use a fixture
3. test_config_override (test_settings.py:22)
Classification: LIKELY_FLAKY (environment + order dependency)
- Monkeypatches os.environ["APP_MODE"] but uses module scope
- Other tests in same module inherit the patched env
- Fix: Use function-scoped monkeypatch or `with mock.patch.dict()`
Example 3: Test that looks suspicious but is actually fine
User: "Is this test flaky? It uses random data."
def test_sorting_algorithm():
random.seed(42)
data = [random.randint(0, 1000) for _ in range(100)]
result = my_sort(data)
assert result == sorted(data)
Approach:
- Read the test — spots
random usage but also random.seed(42) (deterministic)
- Read
my_sort() source — confirms it is a pure function with no side effects
- Check for parallel test execution config — even with parallelism, the seeded RNG is local
Output:
Classification: UNLIKELY_FLAKY
Root cause: None detected
Evidence:
- Random is seeded with fixed seed (42) — output is deterministic
- my_sort() is a pure function with no external dependencies
- No shared state, no I/O, no timing sensitivity
No action needed. This test is well-constructed.
Best Practices
- Do: Always read the production code under test before classifying. The paper's central finding is that test code alone lacks sufficient signal — the production code is where timing, concurrency, and external dependency details live.
- Do: Check build/CI configuration for parallelism settings. Many flakiness issues only manifest under parallel execution, which is invisible in the test code itself.
- Do: Distinguish between unit tests (which can often be classified from code alone if mocking is visible) and integration tests (which almost always require context about external systems).
- Do: Report confidence levels honestly. If you cannot access production code or CI config, say so and downgrade confidence rather than guessing.
- Avoid: Classifying flakiness from test code alone. The paper empirically proved this approach fails — even GPT-4o with chain-of-thought prompting achieved near-random results.
- Avoid: Assuming every
sleep() or random() call indicates flakiness. Context determines whether these patterns are problematic. A seeded random or a sleep that vastly exceeds the operation's maximum latency may be perfectly safe.
Error Handling
- Cannot access production code: If the user only provides the test method, explicitly state that classification confidence is low and request the production code. Cite the paper's finding that test-code-only classification is near random.
- Cannot determine test execution environment: Flag this as a gap. Flakiness often depends on CI parallelism, container resource limits, or network conditions that are not visible in code.
- Ambiguous patterns: When a test has suspicious patterns but also mitigating factors (e.g.,
sleep with a very generous timeout), classify as POSSIBLY_FLAKY and explain both the risk and the mitigation.
- Large test suites: When asked to scan an entire suite, prioritize integration tests, tests with external dependencies, and tests with known CI failure history. Do not attempt to classify hundreds of tests individually — focus on the highest-risk patterns first.
Limitations
- Test code alone is insufficient. This is the paper's core finding and the fundamental constraint. Without production code, configuration, and environment details, flakiness classification accuracy approaches random chance.
- Some flakiness is undetectable from static analysis. Race conditions, resource contention under load, and infrastructure-level issues (DNS resolution, disk I/O variance) cannot be reliably detected by reading code — they require runtime observation or historical failure data.
- Dataset bias. The IDoFT and FlakeFlagger datasets used in the paper are Java-centric. Flakiness patterns in other ecosystems (Python, JavaScript, Go) may differ in prevalence and manifestation.
- LLM classification is not a substitute for reruns. The most reliable flakiness detection remains running tests multiple times (e.g.,
pytest --count=10, Maven Surefire rerunFailingTestsCount). LLM-based analysis is a triage tool, not a definitive oracle.
Reference
Berndt, A., Bekmyradov, V., Gemulla, R., Kessel, M., & Bach, T. (2026). Can We Classify Flaky Tests Using Only Test Code? An LLM-Based Empirical Study. arXiv:2602.05465v1. SANER-RENE 2025. https://arxiv.org/abs/2602.05465v1
Key takeaway: Test-code-only flakiness classification with LLMs (GPT-4o, GPT-4o-mini, CodeLlama) across zero-shot, few-shot, and chain-of-thought prompting yields results near random chance (MCC ~ 0). The critical missing ingredient is project context — production code, build configuration, CI setup, and runtime environment. Any practical flakiness classifier must retrieve this context before reasoning.
1---2name: can-we-classify-flaky3description: Analyze test suites for flaky tests using LLM-based classification with context-augmented reasoning. Applies findings from Berndt et al. (2026) showing that test code alone is insufficient — the skill teaches Claude to gather surrounding project context (configs, dependencies, environment, production code) before classifying. Trigger phrases: 'find flaky tests', 'classify flaky tests', 'detect test flakiness', 'why is this test flaky', 'analyze test reliability', 'flaky test triage'4---56# Flaky Test Classification with Context-Augmented LLM Analysis78This skill enables Claude to analyze test code for flakiness — the property where a test yields inconsistent pass/fail results on the same code revision. Based on empirical findings from Berndt et al. (2026), which demonstrated that LLMs performing zero-shot, few-shot, or chain-of-thought classification on isolated test code perform only marginally better than random guessing, this skill implements a **context-augmented approach**: gathering project artifacts (build configs, production code under test, environment setup, CI configuration) before making a flakiness judgment. The paper's key insight is that flakiness signals live outside the test method itself, so this skill systematically retrieves that missing context.910## When to Use1112- When a user reports intermittent test failures and asks "is this test flaky?"13- When triaging a test suite to identify tests likely to produce non-deterministic results14- When a user pastes a test method and asks why it might be unreliable15- When reviewing a PR that adds new tests and checking for flakiness risk factors16- When a CI pipeline has inconsistent test results and the user wants to identify root causes17- When migrating or refactoring tests and assessing which ones need hardening against flakiness1819## Key Technique2021**Why test code alone fails.** Berndt et al. evaluated GPT-4o, GPT-4o-mini, and CodeLlama across zero-shot, few-shot, and chain-of-thought prompting on two established datasets (IDoFT and FlakeFlagger). Even the best prompt-model combination achieved results only marginally above random chance (MCC near zero). A manual investigation of 50 samples confirmed the root cause: isolated test methods lack the contextual signals needed to determine flakiness. A `Thread.sleep(1000)` in a test might be harmless or catastrophic depending on what it waits for — information that only exists in production code, CI config, or runtime environment.2223**The six flakiness root-cause categories** identified in flaky test literature — and confirmed as undetectable from test code alone — are: (1) **async/timing dependencies** (waits, sleeps, timeouts whose adequacy depends on external systems), (2) **concurrency and shared state** (tests that modify shared resources without isolation), (3) **test-order dependencies** (tests that assume execution order or prior state), (4) **external service dependencies** (network calls, databases, APIs that may be unavailable), (5) **environment sensitivity** (file paths, OS-specific behavior, timezone/locale), and (6) **randomness and non-determinism** (unseeded RNGs, hash iteration order). Detecting most of these requires seeing what the test interacts with, not just the test itself.2425**The context-augmented approach.** Instead of classifying from test code alone (which the paper proves ineffective), this skill implements what the paper recommends as future work: a retrieval-augmented strategy that gathers production code, configuration, and environment details before reasoning about flakiness. This transforms classification from a code-only task into a project-aware analysis.2627## Step-by-Step Workflow28291. **Extract the test method(s) under analysis.** Read the full test file and isolate each test method, preserving class-level setup/teardown (`@Before`, `@After`, `setUp`, `tearDown`, `beforeEach`, etc.) and any shared fields or fixtures.30312. **Identify the production code under test.** Trace imports and method calls from the test to locate the actual classes/functions being tested. Read those source files to understand what the test exercises — this is the single most important context the paper found missing.32333. **Gather build and dependency configuration.** Read `pom.xml`, `build.gradle`, `package.json`, `requirements.txt`, or equivalent. Check for test framework versions, parallelism settings (`forkCount`, `parallel`, `--workers`), and timeout configurations that affect test execution.34354. **Check CI/environment configuration.** Read `.github/workflows/`, `Jenkinsfile`, `.gitlab-ci.yml`, `docker-compose.yml`, or equivalent. Identify whether tests run in parallel, in containers, with specific environment variables, or against external services.36375. **Scan for the six flakiness root-cause categories.** For each test, systematically check:38 - **Timing:** `sleep`, `wait`, `setTimeout`, `Thread.sleep`, polling loops, `@Timeout` annotations39 - **Concurrency:** shared mutable state, static fields, singleton access, missing synchronization40 - **Order dependency:** reliance on database state, file system artifacts, or class-level mutation without cleanup41 - **External services:** HTTP clients, database connections, message queues, file I/O to temp directories42 - **Environment:** hardcoded paths, locale-dependent formatting, OS-specific APIs, port binding43 - **Randomness:** `Math.random()`, `Random` without seed, `UUID.randomUUID()` in assertions, `HashMap` iteration44456. **Cross-reference test signals with production context.** A `Thread.sleep(500)` is only problematic if the operation it waits on can exceed 500ms. A database call is only flaky if the test uses a real (not mocked) connection. This step resolves ambiguities that the paper showed are impossible to resolve from test code alone.46477. **Classify each test with confidence and rationale.** Assign one of: `LIKELY_FLAKY` (clear root cause identified with context), `POSSIBLY_FLAKY` (suspicious patterns but mitigating factors present), or `UNLIKELY_FLAKY` (no detectable risk factors). Always state the specific root-cause category and the evidence from both test and production code.48498. **Recommend targeted fixes.** For each flaky or possibly-flaky test, provide a concrete remediation: replace `sleep` with polling/await, mock external services, add test isolation, seed random generators, use `@TempDir` instead of hardcoded paths, etc.5051## Concrete Examples5253**Example 1: Timing-dependent test in a Java project**5455User: "This test fails intermittently in CI. Is it flaky?"56```java57@Test58public void testAsyncNotification() {59 notificationService.sendAsync("user@example.com", "Hello");60 Thread.sleep(2000);61 List<Notification> sent = notificationRepository.findAll();62 assertEquals(1, sent.size());63}64```6566Approach:671. Read the test — sees `Thread.sleep(2000)` and async call pattern682. Read `NotificationService.sendAsync()` source — discovers it uses a thread pool with configurable queue size and sends via SMTP693. Read CI config — finds tests run with `-DforkCount=4` (parallel execution) on a shared CI runner704. Check `application-test.properties` — finds it points to a real SMTP test server, not a mock7172Output:73```74Classification: LIKELY_FLAKY75Root cause: Async/timing dependency76Confidence: High7778Evidence:79- Test code: Thread.sleep(2000) used as a hard wait for async operation80- Production code: NotificationService.sendAsync() submits to thread pool, then81 sends SMTP email — total latency depends on SMTP server response time82- CI config: 4 parallel forks compete for resources, increasing latency variance83- No mock: Test hits a real SMTP server, adding network non-determinism8485Recommendation:86Replace Thread.sleep with Awaitility polling:87 await().atMost(10, SECONDS)88 .untilAsserted(() ->89 assertEquals(1, notificationRepository.findAll().size()));90Consider mocking the SMTP transport in unit tests.91```9293**Example 2: Shared-state test in a Python project**9495User: "Some tests pass alone but fail when the full suite runs. Help me find which ones are flaky."9697Approach:981. Scan test files for shared module-level state: global variables, class attributes, singleton patterns992. Read `conftest.py` fixtures — check scope (`session`, `module`, `function`) and whether fixtures mutate shared state1003. Read `pytest.ini` / `pyproject.toml` — check for `--randomly-seed`, parallel execution (`-n auto`), test ordering plugins1014. Identify tests that write to shared resources (database, files, caches) without cleanup102103Output:104```105Found 3 tests with order-dependency risk:1061071. test_user_creation (test_auth.py:45)108 Classification: LIKELY_FLAKY (order dependency)109 - Inserts into `users` table using module-scoped db fixture110 - No teardown/rollback — subsequent tests see leftover rows111 - Fix: Use function-scoped fixture with transaction rollback1121132. test_cache_expiry (test_cache.py:78)114 Classification: POSSIBLY_FLAKY (shared state)115 - Reads from module-level `_cache = {}` dict populated by test_cache_set116 - If test_cache_set runs first, passes; otherwise, KeyError117 - Fix: Initialize cache in each test or use a fixture1181193. test_config_override (test_settings.py:22)120 Classification: LIKELY_FLAKY (environment + order dependency)121 - Monkeypatches os.environ["APP_MODE"] but uses module scope122 - Other tests in same module inherit the patched env123 - Fix: Use function-scoped monkeypatch or `with mock.patch.dict()`124```125126**Example 3: Test that looks suspicious but is actually fine**127128User: "Is this test flaky? It uses random data."129```python130def test_sorting_algorithm():131 random.seed(42)132 data = [random.randint(0, 1000) for _ in range(100)]133 result = my_sort(data)134 assert result == sorted(data)135```136137Approach:1381. Read the test — spots `random` usage but also `random.seed(42)` (deterministic)1392. Read `my_sort()` source — confirms it is a pure function with no side effects1403. Check for parallel test execution config — even with parallelism, the seeded RNG is local141142Output:143```144Classification: UNLIKELY_FLAKY145Root cause: None detected146147Evidence:148- Random is seeded with fixed seed (42) — output is deterministic149- my_sort() is a pure function with no external dependencies150- No shared state, no I/O, no timing sensitivity151152No action needed. This test is well-constructed.153```154155## Best Practices156157- **Do:** Always read the production code under test before classifying. The paper's central finding is that test code alone lacks sufficient signal — the production code is where timing, concurrency, and external dependency details live.158- **Do:** Check build/CI configuration for parallelism settings. Many flakiness issues only manifest under parallel execution, which is invisible in the test code itself.159- **Do:** Distinguish between unit tests (which can often be classified from code alone if mocking is visible) and integration tests (which almost always require context about external systems).160- **Do:** Report confidence levels honestly. If you cannot access production code or CI config, say so and downgrade confidence rather than guessing.161- **Avoid:** Classifying flakiness from test code alone. The paper empirically proved this approach fails — even GPT-4o with chain-of-thought prompting achieved near-random results.162- **Avoid:** Assuming every `sleep()` or `random()` call indicates flakiness. Context determines whether these patterns are problematic. A seeded random or a sleep that vastly exceeds the operation's maximum latency may be perfectly safe.163164## Error Handling165166- **Cannot access production code:** If the user only provides the test method, explicitly state that classification confidence is low and request the production code. Cite the paper's finding that test-code-only classification is near random.167- **Cannot determine test execution environment:** Flag this as a gap. Flakiness often depends on CI parallelism, container resource limits, or network conditions that are not visible in code.168- **Ambiguous patterns:** When a test has suspicious patterns but also mitigating factors (e.g., `sleep` with a very generous timeout), classify as `POSSIBLY_FLAKY` and explain both the risk and the mitigation.169- **Large test suites:** When asked to scan an entire suite, prioritize integration tests, tests with external dependencies, and tests with known CI failure history. Do not attempt to classify hundreds of tests individually — focus on the highest-risk patterns first.170171## Limitations172173- **Test code alone is insufficient.** This is the paper's core finding and the fundamental constraint. Without production code, configuration, and environment details, flakiness classification accuracy approaches random chance.174- **Some flakiness is undetectable from static analysis.** Race conditions, resource contention under load, and infrastructure-level issues (DNS resolution, disk I/O variance) cannot be reliably detected by reading code — they require runtime observation or historical failure data.175- **Dataset bias.** The IDoFT and FlakeFlagger datasets used in the paper are Java-centric. Flakiness patterns in other ecosystems (Python, JavaScript, Go) may differ in prevalence and manifestation.176- **LLM classification is not a substitute for reruns.** The most reliable flakiness detection remains running tests multiple times (e.g., `pytest --count=10`, Maven Surefire `rerunFailingTestsCount`). LLM-based analysis is a triage tool, not a definitive oracle.177178## Reference179180Berndt, A., Bekmyradov, V., Gemulla, R., Kessel, M., & Bach, T. (2026). *Can We Classify Flaky Tests Using Only Test Code? An LLM-Based Empirical Study.* arXiv:2602.05465v1. SANER-RENE 2025. [https://arxiv.org/abs/2602.05465v1](https://arxiv.org/abs/2602.05465v1)181182Key takeaway: Test-code-only flakiness classification with LLMs (GPT-4o, GPT-4o-mini, CodeLlama) across zero-shot, few-shot, and chain-of-thought prompting yields results near random chance (MCC ~ 0). The critical missing ingredient is project context — production code, build configuration, CI setup, and runtime environment. Any practical flakiness classifier must retrieve this context before reasoning.