Skip to main content

Testing Guide

Comprehensive testing with 338+ tests.
Complete testing documents in your project: testing-documents.md

Test Suite Overview

Unit Tests

251 tests - ViewModels, repositories, clients

Integration Tests

47 tests - Component interaction

Snapshot Tests

12 tests - UI rendering verification

UI Tests

28 tests - End-to-end flows
Total: 338+

Run Tests

  • Xcode
  • Command Line
# Run all tests
 + U

# Run specific test
Click diamond icon next to test function

# Run test class
Click diamond icon next to class name

Coverage Goals

ModuleTargetStatus
Core96%+
Networking93%+
Storage88%+
Auth87%+
AI91%+
Payments82%+
FeatureChat78%+
Overall85-90%✅ CI Enforced

Writing Tests

Unit Test Example

@Test func sendMessage() async throws {
    // Arrange
    let mockLLM = MockLLMClient()
    let mockRepo = MockMessageRepository()
    let viewModel = ChatViewModel(
        llmClient: mockLLM,
        messageRepo: mockRepo
    )
    
    // Act
    await viewModel.sendMessage("Hello")
    
    // Assert
    #expect(viewModel.messages.count == 2)  // user + AI
    #expect(mockRepo.saveCallCount == 2)
}

Integration Test Example

@Test func compositionRoot() {
    // Arrange
    let composition = CompositionRoot()
    
    // Assert - All dependencies created
    #expect(composition.httpClient != nil)
    #expect(composition.sessionManager != nil)
    
    // Assert - Factories work
    let chatVM = composition.makeChatViewModel(conversationID: uuid)
    #expect(chatVM != nil)
}

UI Test Example

func testChatFlow() throws {
    let app = XCUIApplication()
    app.launch()
    
    // Navigate to chat
    app.buttons["Start Chat"].tap()
    
    // Send message
    let input = app.textFields["messageInput"]
    input.tap()
    input.typeText("Hello")
    app.buttons["send"].tap()
    
    // Verify
    XCTAssertTrue(app.staticTexts["Hello"].exists)
}

CI/CD Integration

Automated Testing

Tests run automatically on:
  • ✅ Every push to main/develop
  • ✅ All pull requests
  • ✅ Coverage gate: Fails if < 85%
  • ✅ SwiftLint enforcement

Coverage Reports

  • Automatic PR comments with coverage stats
  • Weekly comprehensive reports
  • HTML reports with detailed breakdowns
  • Issue creation for low coverage modules

Test Organization

Tests/
├── Unit Tests (251)
│   - ViewModels
│   - Repositories
│   - Clients
│   - Utilities

├── Integration Tests (47)
│   - Component wiring
│   - DI validation
│   - Module interaction

├── Snapshot Tests (12)
│   - UI rendering
│   - Light/dark mode
│   - Accessibility states

└── UI Tests (28)
    - Auth flows
    - Chat flows
    - Payment flows
    - Settings flows

Best Practices

Do test:
  • ✅ ViewModels (business logic)
  • ✅ Repositories (data access)
  • ✅ Clients (external services)
  • ✅ Error scenarios
  • ✅ Edge cases
Don’t test:
  • ❌ SwiftUI Views (snapshot instead)
  • ❌ Third-party libraries
  • ❌ Framework code
  • Use Arrange-Act-Assert pattern
  • One assertion per test (when possible)
  • Clear test names (testSendMessage_WhenOffline_ShowsError)
  • Mock external dependencies
  • Test in isolation
  • Protocol-based mocking
  • Track call counts
  • Configurable behavior
  • Avoid over-mocking
  • Use real objects when simple

Coverage Measurement

# Generate HTML report
./scripts/run-tests.sh --coverage --open

# Check specific module
./scripts/run-tests.sh --package Core --coverage

# CI enforcement
# Builds fail if coverage < 85%

View Complete Guide

View Complete Testing Guide

Complete guide with examples, patterns, and best practices

Troubleshooting

  • Check iOS version match
  • Verify simulator name
  • Clear derived data
  • Check for timing issues
  • Run with —coverage flag
  • Check for untested branches
  • Add error scenario tests
  • Test async code properly
  • Add explicit waits
  • Use accessibility identifiers
  • Avoid animations in tests
  • Check for race conditions
I