iOS Testing Strategy

Last Updated: 2025-11-21


Current State

Manual Testing Only

  • All iOS features tested manually on simulator
  • No automated UI tests
  • No unit tests for ViewModels
  • Smoke tests documented in PR descriptions

Risk: Regressions not caught until user testing


Recommendation: Add XCUITest Coverage

Phase 2f: iOS UI Testing

Priority: Medium (deferred but recommended before Phase 2 completion) Effort: 8-10 hours

Setup

  1. Create UI test target in Xcode
  2. Configure test schemes
  3. Set up test data fixtures
  4. Create helper methods for common interactions

Test Coverage Goals

Phase 2a: Round Creation

  • ✅ Quick Start templates fill correctly
  • ✅ Manual entry with validation
  • ✅ Error states display
  • ✅ Form reset works

Phase 2d: Active Scoring

  • ✅ Score entry workflow
  • ✅ End completion
  • ✅ Undo functionality
  • ✅ Running total calculations

Phase 2c: Round List

  • ✅ List displays rounds
  • ✅ Navigation works
  • ✅ Empty state
  • ✅ Pull-to-refresh

Example XCUITest

import XCTest
 
class RoundCreationUITests: XCTestCase {
    var app: XCUIApplication!
 
    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication()
        app.launch()
    }
 
    func testQuickStart18mIndoor() throws {
        // Navigate to Rounds tab
        app.tabBars.buttons["Rounds"].tap()
 
        // Tap Create Round
        app.navigationBars.buttons["plus"].tap()
 
        // Tap 18m Indoor template
        app.buttons["18m Indoor"].tap()
 
        // Verify form fields populated
        XCTAssertEqual(app.textFields["Round Name"].value as? String, "18m Indoor Practice")
        XCTAssertEqual(app.textFields["Ends"].value as? String, "10")
        XCTAssertEqual(app.textFields["Arrows per End"].value as? String, "3")
 
        // Verify summary card
        XCTAssertTrue(app.staticTexts["Total Arrows: 30"].exists)
 
        // Create round
        app.buttons["Create Round"].tap()
 
        // Verify success (navigation or message)
        // Implementation depends on Phase 2b/2d navigation
    }
 
    func testValidationPreventsInvalidSubmission() throws {
        app.tabBars.buttons["Rounds"].tap()
        app.navigationBars.buttons["plus"].tap()
 
        // Clear Round Name
        let roundNameField = app.textFields["Round Name"]
        roundNameField.tap()
        roundNameField.clearText()
 
        // Verify Create button disabled
        XCTAssertFalse(app.buttons["Create Round"].isEnabled)
    }
}
 
extension XCUIElement {
    func clearText() {
        guard let stringValue = self.value as? String else {
            return
        }
 
        var deleteString = String()
        for _ in stringValue {
            deleteString += XCUIKeyboardKey.delete.rawValue
        }
        self.typeText(deleteString)
    }
}

CI/CD Integration

Future Consideration: GitHub Actions for iOS

name: iOS Tests
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run tests
        run: |
          cd iosApp
          xcodebuild test \
            -scheme ArcheryApprentice \
            -destination 'platform=iOS Simulator,name=iPhone 15,OS=latest'

Success Criteria

Phase 2 testing complete when:

  • ✅ XCUITest target configured
  • ✅ Round creation tests passing
  • ✅ Score entry tests passing
  • ✅ Round list tests passing
  • ✅ Test coverage matches Android level

Recommendation: Implement Phase 2f before marking Phase 2 complete to establish quality baseline for future phases.