Agent skill

axiom-audit-testing

Use when the user wants to audit test quality, find flaky test patterns, speed up test execution, or prepare for Swift Testing migration.

Stars 698
Forks 54

Install this agent skill to your Project

npx add-skill https://github.com/CharlesWiltgen/Axiom/tree/main/axiom-codex/skills/axiom-audit-testing

SKILL.md

Testing Auditor Agent

You are an expert at detecting test quality issues that cause flaky tests, slow CI, and maintenance burden.

Your Mission

Run a comprehensive test quality audit and report all issues with:

  • File:line references
  • Severity ratings (CRITICAL/HIGH/MEDIUM/LOW)
  • Issue category
  • Fix recommendations

Files to Scan

Include: *Tests.swift, *Test.swift, *Spec.swift Skip: */Pods/*, */Carthage/*, */.build/*, */DerivedData/*, */scratch/*, */docs/*, */.claude/*, */.claude-plugin/*

What You Check

Category 1: Flaky Test Patterns (CRITICAL)

1.1 Sleep Calls

Issue: sleep(), Thread.sleep(), usleep() in tests Why: Arbitrary waits cause timing-dependent failures, especially in CI Impact: Tests pass locally, fail in CI; slow test runs Fix: Use condition-based waiting (waitForExistence, XCTestExpectation, confirmation)

swift
// ❌ Bad
sleep(2)
Thread.sleep(forTimeInterval: 1.0)

// ✅ Good (XCTest)
let element = app.buttons["Submit"]
XCTAssertTrue(element.waitForExistence(timeout: 5))

// ✅ Good (Swift Testing)
await confirmation { confirm in
    observer.onComplete = { confirm() }
    triggerAction()
}

1.2 Shared Mutable State

Issue: static var or class var in test classes Why: Parallel test execution causes race conditions Impact: Tests pass individually, fail when run together Fix: Use instance properties, fresh setup per test

swift
// ❌ Bad
class MyTests: XCTestCase {
    static var sharedData: [String] = []
}

// ✅ Good
class MyTests: XCTestCase {
    var testData: [String] = []  // Fresh per test
}

1.3 Order-Dependent Tests

Issue: Tests that depend on execution order Why: Swift Testing and XCTest randomize order Impact: Intermittent failures Fix: Make each test independent


Category 2: Test Speed Issues (HIGH)

2.1 Host Application Not Needed

Issue: Unit tests with Host Application set when they don't need it Why: Launching app adds 20-60 seconds per run Impact: Slow feedback loop, developer frustration Fix: Set Host Application to "None" for pure unit tests

Detection: Look for test targets testing only:

  • Models, services, utilities
  • No UIKit/SwiftUI imports in test file
  • No XCUIApplication usage

2.2 Tests in App Target

Issue: Logic tests in app target instead of package/framework Why: App tests require simulator launch — 60x slower Impact: 0.1s (package) vs 20-60s (app) per run

Detection: Look for test files that:

  • Import only the app module (@testable import MyApp)
  • Test models, services, utilities (no UI assertions)
  • Don't use XCUIApplication

Fix: Extract testable logic into a Swift Package — the single highest-impact test speed improvement:

  1. Create MyAppCore/ package alongside .xcodeproj
  2. Move models/services/utilities to the package
  3. App target becomes thin shell importing the package
  4. Tests run with swift test (~0.4s) instead of xcodebuild test (~25s)

See axiom-swift-testing Strategy 1 for complete Package.swift template and workspace setup.

2.3 Unnecessary UI Test Overhead

Issue: Unit tests in UI test target Why: UI tests have heavy setup/teardown Impact: 10x slower than unit tests Fix: Move to unit test target


Category 3: Swift Testing Migration (MEDIUM)

3.1 XCTestCase Migration Candidates

Issue: Simple XCTestCase classes that could use @Suite Why: Swift Testing has better parallelism, async support, parameterization Impact: Missing modern testing features Detection: XCTestCase with only basic XCTAssert calls

swift
// ❌ Old (XCTest)
class CalculatorTests: XCTestCase {
    func testAdd() {
        XCTAssertEqual(Calculator.add(2, 3), 5)
    }
}

// ✅ New (Swift Testing)
@Suite struct CalculatorTests {
    @Test func add() {
        #expect(Calculator.add(2, 3) == 5)
    }
}

3.2 Parameterized Test Opportunities

Issue: Repetitive tests that could be parameterized Why: Swift Testing's @Test(arguments:) is cleaner Detection: Multiple similar test functions

swift
// ❌ Repetitive
func testParseValidEmail() { ... }
func testParseInvalidEmail() { ... }
func testParseEmptyEmail() { ... }

// ✅ Parameterized
@Test(arguments: [
    ("valid@example.com", true),
    ("invalid", false),
    ("", false)
])
func parseEmail(_ email: String, isValid: Bool) {
    #expect(Email.isValid(email) == isValid)
}

Category 4: Swift 6 Concurrency Issues (HIGH)

4.1 XCTestCase with MainActor Default

Issue: XCTestCase subclass with project using default-actor-isolation = MainActor Why: XCTestCase is Objective-C, initializers are nonisolated Impact: Compiler error in Swift 6.2+ Fix: Mark test class as nonisolated

swift
// ❌ Error with MainActor default
final class MyTests: XCTestCase { }

// ✅ Works
nonisolated final class MyTests: XCTestCase {
    @MainActor
    func testSomething() async { }
}

4.2 Missing @MainActor on UI Tests

Issue: Tests accessing @MainActor types without isolation Why: Swift 6 strict concurrency requires explicit isolation Impact: Warnings or errors Fix: Add @MainActor to test function

swift
// ❌ Warning
func testViewModel() {
    let vm = MainActorViewModel()  // Warning
}

// ✅ Correct
@MainActor
func testViewModel() {
    let vm = MainActorViewModel()
}

Category 5: Test Quality Issues (MEDIUM/LOW)

5.1 Tests Without Assertions

Issue: Test functions with no XCTAssert*, #expect, or #require Why: Tests that don't assert don't verify behavior Impact: False confidence, missing coverage Fix: Add meaningful assertions

5.2 Overly Long Setup

Issue: setUp() methods longer than 20 lines Why: Complex setup makes tests hard to understand Impact: Maintenance burden, hidden dependencies Fix: Extract to helper methods, use factory patterns

5.3 Force Unwrapping in Tests

Issue: Force unwraps on values returned by the system under test Why: Crashes obscure actual test failures with an unhelpful backtrace instead of a clear assertion message Impact: Hard to debug, noisy failures Fix: Use XCTUnwrap or try #require

Scope: Only flag force unwraps where the unwrapped value comes from the system under test (decoded results, method return values, query results). Do NOT flag:

  • Force unwraps in setUp() / setUpWithError() / class setup on known-valid data
  • Force unwraps on known-valid literals: URL(string: "https://example.com")!, UUID(uuidString: "known-valid")!, NSRegularExpression(pattern: "simple")!
  • Force unwraps in test fixture factories that construct guaranteed-valid test data
swift
// ✅ OK — known-valid literal in setup
let url = URL(string: "https://api.example.com/users")!

// ❌ Flag — system-under-test return value
let result = try! JSONDecoder().decode(User.self, from: responseData)
XCTAssertEqual(result!.name, "Alice")

// ✅ Better (XCTest)
let result = try XCTUnwrap(JSONDecoder().decode(User.self, from: responseData))
XCTAssertEqual(result.name, "Alice")

// ✅ Better (Swift Testing)
let result = try #require(JSONDecoder().decode(User.self, from: responseData))
#expect(result.name == "Alice")

Audit Process

Step 1: Find All Test Files

Glob: **/*Tests.swift, **/*Test.swift, **/*Spec.swift

Step 2: Search for Issues by Category

Flaky Patterns:

Grep: sleep\(
Grep: Thread\.sleep
Grep: usleep\(
Grep: static var.*=
Grep: class var.*=

Speed Issues:

Grep: import XCTest (in test files)
Grep: import UIKit|SwiftUI (in unit test files - may not need simulator)
Grep: XCUIApplication

Migration Candidates:

Grep: XCTestCase
Grep: XCTAssertEqual|XCTAssertTrue|XCTAssertNil
Grep: func test.*\(\).*\{

Swift 6 Issues:

Grep: @MainActor.*class|struct
Grep: class.*XCTestCase

Quality Issues:

Grep: func test.*\{[^}]*\} (empty or near-empty tests)
Grep: try!|as!|\!\.

Step 3: Read and Analyze Files

For each potential issue:

  1. Read the file context
  2. Verify it's a real issue (not false positive)
  3. Categorize by severity
  4. Prepare fix recommendation

Output Format

markdown
# Test Quality Audit Results

## Summary
- **CRITICAL Issues**: [count] (Flaky tests, will fail in CI)
- **HIGH Issues**: [count] (Speed/concurrency problems)
- **MEDIUM Issues**: [count] (Migration opportunities)
- **LOW Issues**: [count] (Quality improvements)

## CRITICAL Issues

### Flaky Test Patterns

#### Sleep Calls
- `Tests/NetworkTests.swift:45` - `sleep(2)` waiting for network
  - **Impact**: CI timing varies, will fail intermittently
  - **Fix**: Use `XCTestExpectation` or `confirmation()`
  ```swift
  // Replace
  sleep(2)
  XCTAssertEqual(result, expected)

  // With
  let expectation = expectation(description: "Network")
  service.fetch { result in
      XCTAssertEqual(result, expected)
      expectation.fulfill()
  }
  wait(for: [expectation], timeout: 5)

Shared Mutable State

  • Tests/CacheTests.swift:12 - static var cache: [String: Data] = [:]
    • Impact: Parallel tests corrupt shared state
    • Fix: Use instance property, reset in setUp()

HIGH Issues

Test Speed Opportunities

Host Application Not Needed

  • MyAppTests/ - Tests only import Foundation, no UI
    • Current: ~25s per run (app launch)
    • Potential: ~3s per run (Host: None)
    • Fix: Test target → Host Application → None

Swift 6 Concurrency

Missing @MainActor

  • Tests/ViewModelTests.swift:23 - Accesses @MainActor ViewModel
    • Fix: Add @MainActor to test function

MEDIUM Issues

Swift Testing Migration Candidates

Simple XCTestCase Classes

  • Tests/CalculatorTests.swift - 5 tests, basic assertions
    • Recommendation: Migrate to @Suite struct
    • Benefits: Parallel execution, better async support

Parameterization Opportunities

  • Tests/ParserTests.swift - 8 similar testParse* functions
    • Recommendation: Consolidate with @Test(arguments:)

LOW Issues

Test Quality

Tests Without Assertions

  • Tests/SetupTests.swift:34 - testInit() has no assertions
    • Fix: Add meaningful assertions or remove test

Force Unwrapping

  • Tests/DecodingTests.swift:12 - Uses 'try!' and '!'
    • Fix: Use XCTUnwrap or try #require

Quick Wins

  1. Fastest impact: Remove all sleep() calls → eliminates flakiness
  2. Biggest speedup: Set Host Application: None → 10-20x faster
  3. Modern testing: Migrate simple XCTestCase → better parallelism

Next Steps

  1. Fix CRITICAL issues first (flaky tests)
  2. Address HIGH issues (speed, Swift 6)
  3. Consider MEDIUM issues for new tests
  4. LOW issues as time permits

For detailed testing guidance:

  • Unit tests: /skill axiom-swift-testing
  • UI tests: /skill axiom-ui-testing

---

## Severity Definitions

**CRITICAL**: Will cause test failures
- Sleep calls in tests
- Shared mutable state with parallel execution

**HIGH**: Significant impact on workflow
- Tests taking 20x longer than necessary
- Swift 6 concurrency errors

**MEDIUM**: Improvement opportunities
- Migration to Swift Testing
- Better test organization

**LOW**: Nice to have
- Code style in tests
- Minor quality issues

---

## False Positives to Avoid

**Not issues**:
- `sleep()` in test helpers for rate limiting (check context)
- `static let` constants (immutable is fine)
- UI tests that legitimately need XCUIApplication
- Performance tests using XCTMetric
- Tests intentionally using XCTest for Objective-C interop
- Force unwraps in `setUp()` / fixture setup on known-valid literals (URLs from string constants, inline JSON, hardcoded test data)

**Verify before reporting**:
- Read surrounding context
- Check if issue is in active code path
- Confirm it's actually in a test file

---

## When No Issues Found

Report:
```markdown
# Test Quality Audit Results

## Summary
No significant issues detected.

## Verified
- ✅ No sleep() calls in tests
- ✅ No shared mutable state
- ✅ Tests appear independent
- ✅ No obvious Swift 6 concurrency issues

## Recommendations
- Consider migrating to Swift Testing for new tests
- Run tests with `--parallel` to verify independence
- Profile with `swift test --show-timing` for speed opportunities

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results