Testing Strategies

Unit Testing with Mocks

SuggestKit provides mock implementations for testing:
import XCTest
@testable import YourApp
@testable import SuggestKit

class SuggestKitTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
        // Configure mock mode for testing
        SuggestKit.configureMock(
            mockSuggestions: true,
            mockUsers: true,
            simulateNetworkDelay: 0.1
        )
    }
    
    func testCreateSuggestion() async throws {
        // Given
        let title = "Test Suggestion"
        let description = "This is a test suggestion"
        let userId = "test_user_123"
        
        // When
        let suggestion = try await SuggestKit.createSuggestion(
            title: title,
            description: description,
            userId: userId
        )
        
        // Then
        XCTAssertEqual(suggestion.title, title)
        XCTAssertEqual(suggestion.description, description)
        XCTAssertEqual(suggestion.userId, userId)
        XCTAssertEqual(suggestion.status, .pending)
    }
    
    func testVotingBehavior() async throws {
        // Given
        let suggestion = try await SuggestKit.createSuggestion(
            title: "Vote Test",
            description: "Testing votes",
            userId: "user_1"
        )
        
        // When - Vote for suggestion
        let vote = try await SuggestKit.voteForSuggestion(
            suggestionId: suggestion.id,
            userId: "user_2"
        )
        
        // Then
        XCTAssertNotNil(vote)
        
        // Verify suggestion vote count updated
        let updatedSuggestion = try await SuggestKit.getSuggestion(id: suggestion.id)
        XCTAssertEqual(updatedSuggestion.voteCount, 1)
    }
}

UI Testing

Test SuggestKit UI components in your app:
import XCUITest

class SuggestKitUITests: XCTestCase {
    
    func testFeedbackFlowEndToEnd() throws {
        let app = XCUIApplication()
        app.launch()
        
        // Tap feedback button
        app.buttons["Send Feedback"].tap()
        
        // Verify SuggestKit view appears
        XCTAssertTrue(app.navigationBars["Feedback"].exists)
        
        // Fill out suggestion form
        let titleField = app.textFields["Suggestion Title"]
        titleField.tap()
        titleField.typeText("UI Test Suggestion")
        
        let descriptionField = app.textViews["Description"]
        descriptionField.tap()
        descriptionField.typeText("This suggestion was created during UI testing")
        
        // Submit suggestion
        app.buttons["Submit"].tap()
        
        // Verify success state
        XCTAssertTrue(app.alerts["Success"].exists)
    }
    
    func testVoteInteraction() throws {
        let app = XCUIApplication()
        app.launch()
        
        // Navigate to feedback view
        app.buttons["Send Feedback"].tap()
        
        // Find first suggestion and vote
        let firstSuggestion = app.cells.firstMatch
        let voteButton = firstSuggestion.buttons["Vote"]
        
        let initialVoteCount = voteButton.label
        voteButton.tap()
        
        // Verify vote count increased
        XCTAssertNotEqual(voteButton.label, initialVoteCount)
    }
}

Mock Configuration

Basic Mock Setup

#if DEBUG
extension SuggestKit {
    static func setupForTesting() {
        configureMock(
            mockSuggestions: true,
            mockUsers: true,
            mockVotes: true,
            mockNetworkDelay: 0.1,
            simulateOfflineMode: false
        )
    }
}
#endif

Custom Mock Data

extension SuggestKit {
    static func configureMockData() {
        setMockSuggestions([
            MockSuggestion(
                id: "mock_1",
                title: "Add Dark Mode",
                description: "Please add dark mode support",
                status: .pending,
                voteCount: 12,
                hasUserVoted: false
            ),
            MockSuggestion(
                id: "mock_2", 
                title: "Improve Performance",
                description: "The app feels slow on older devices",
                status: .inProgress,
                voteCount: 8,
                hasUserVoted: true
            )
        ])
        
        setMockUsers([
            MockUser(
                id: "mock_user_1",
                email: "test@example.com",
                monthlyRevenue: 19.99
            )
        ])
    }
}

Preview Testing

SwiftUI Previews with Mock Data

struct SuggestKitView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            // Default state
            SuggestKitView()
                .onAppear {
                    SuggestKit.configureMock()
                }
                .previewDisplayName("Default")
            
            // Empty state
            SuggestKitView()
                .onAppear {
                    SuggestKit.configureMock(mockSuggestions: [])
                }
                .previewDisplayName("Empty State")
            
            // Loading state
            SuggestKitView()
                .onAppear {
                    SuggestKit.configureMock(simulateLoading: true)
                }
                .previewDisplayName("Loading")
            
            // Error state
            SuggestKitView()
                .onAppear {
                    SuggestKit.configureMock(simulateError: true)
                }
                .previewDisplayName("Error State")
            
            // Dark mode
            SuggestKitView()
                .onAppear {
                    SuggestKit.configureMock()
                }
                .preferredColorScheme(.dark)
                .previewDisplayName("Dark Mode")
        }
    }
}

Integration Testing

Network Testing

class NetworkIntegrationTests: XCTestCase {
    
    func testRealAPIConnection() async throws {
        // Configure with test API key
        SuggestKit.configure(
            apiKey: "public_test_key_here",
            environment: .sandbox
        )
        
        // Test user creation
        let user = try await SuggestKit.createUser(
            appUserId: "integration_test_\(UUID().uuidString)",
            userPlatform: .iOS,
            locale: "en_US",
            email: "test@example.com"
        )
        
        XCTAssertNotNil(user.id)
        
        // Test suggestion creation
        let suggestion = try await SuggestKit.createSuggestion(
            title: "Integration Test Suggestion",
            description: "Created during integration testing",
            userId: user.id
        )
        
        XCTAssertNotNil(suggestion.id)
        XCTAssertEqual(suggestion.title, "Integration Test Suggestion")
        
        // Test voting
        let otherUser = try await SuggestKit.createUser(
            appUserId: "voter_\(UUID().uuidString)",
            userPlatform: .iOS,
            locale: "en_US"
        )
        
        let vote = try await SuggestKit.voteForSuggestion(
            suggestionId: suggestion.id,
            userId: otherUser.id
        )
        
        XCTAssertNotNil(vote.id)
    }
}

Offline Testing

class OfflineTests: XCTestCase {
    
    func testOfflineFunctionality() async throws {
        // Configure for offline testing
        SuggestKit.configure(
            apiKey: "public_test_key",
            environment: .sandbox
        )
        
        // Simulate offline mode
        SuggestKit.simulateOfflineMode(true)
        
        // Create suggestion while offline
        let suggestion = try await SuggestKit.createSuggestion(
            title: "Offline Suggestion",
            description: "Created while offline",
            userId: "test_user"
        )
        
        // Verify it's queued for sync
        XCTAssertTrue(SuggestKit.hasPendingSync)
        XCTAssertEqual(SuggestKit.pendingSyncCount, 1)
        
        // Go back online and sync
        SuggestKit.simulateOfflineMode(false)
        try await SuggestKit.sync()
        
        // Verify sync completed
        XCTAssertFalse(SuggestKit.hasPendingSync)
        XCTAssertEqual(SuggestKit.pendingSyncCount, 0)
    }
}

Performance Testing

Load Testing

class PerformanceTests: XCTestCase {
    
    func testSuggestionListPerformance() throws {
        measure {
            // Test rendering large suggestion lists
            let suggestions = (1...1000).map { index in
                MockSuggestion(
                    id: "perf_\(index)",
                    title: "Performance Test \(index)",
                    description: "Description for suggestion \(index)",
                    voteCount: Int.random(in: 0...50)
                )
            }
            
            SuggestKit.setMockSuggestions(suggestions)
            
            // Measure time to load and display
            _ = SuggestKitView()
        }
    }
    
    func testVotingPerformance() throws {
        let expectation = expectation(description: "Batch voting")
        
        Task {
            let startTime = CFAbsoluteTimeGetCurrent()
            
            // Simulate rapid voting
            for i in 1...100 {
                try await SuggestKit.voteForSuggestion(
                    suggestionId: "perf_suggestion",
                    userId: "user_\(i)"
                )
            }
            
            let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
            
            // Should complete within reasonable time
            XCTAssertLessThan(timeElapsed, 5.0, "Batch voting took too long")
            
            expectation.fulfill()
        }
        
        waitForExpectations(timeout: 10.0)
    }
}

Debug Testing

Logging and Debugging

#if DEBUG
class DebugTests: XCTestCase {
    
    func testDebugLogging() {
        // Enable verbose logging
        SuggestKit.configure(
            apiKey: "test_key",
            environment: .sandbox,
            debugMode: true,
            logLevel: .verbose
        )
        
        // Capture log output
        let logCapture = LogCapture()
        SuggestKit.setLogHandler(logCapture.handleLog)
        
        // Perform actions that should log
        Task {
            try await SuggestKit.createUser(
                appUserId: "debug_user",
                userPlatform: .iOS,
                locale: "en_US"
            )
        }
        
        // Verify logs were generated
        XCTAssertTrue(logCapture.logs.contains { $0.contains("Creating user") })
    }
}

class LogCapture {
    var logs: [String] = []
    
    func handleLog(_ message: String, level: LogLevel) {
        logs.append("[\(level)] \(message)")
    }
}
#endif

Memory Testing

class MemoryTests: XCTestCase {
    
    func testMemoryLeaks() {
        weak var weakSuggestKitView: SuggestKitView?
        
        autoreleasepool {
            let view = SuggestKitView()
            weakSuggestKitView = view
            
            // Use the view
            _ = view.body
        }
        
        // View should be deallocated
        XCTAssertNil(weakSuggestKitView, "SuggestKitView has a memory leak")
    }
}

Test Automation

CI/CD Integration

# .github/workflows/ios-tests.yml
name: iOS Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: macos-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Xcode
      uses: maxim-lobanov/setup-xcode@v1
      with:
        xcode-version: latest-stable
    
    - name: Run Tests
      run: |
        xcodebuild test \
          -scheme YourApp \
          -destination 'platform=iOS Simulator,name=iPhone 14,OS=latest' \
          -testPlan SuggestKitTests
    
    - name: Upload Coverage
      uses: codecov/codecov-action@v2
      with:
        file: ./DerivedData/Logs/Test/*.xcresult

Test Data Management

class TestDataManager {
    static func createTestUser() async throws -> User {
        return try await SuggestKit.createUser(
            appUserId: "test_\(UUID().uuidString)",
            userPlatform: .iOS,
            locale: "en_US",
            email: "test@example.com"
        )
    }
    
    static func createTestSuggestion(userId: String) async throws -> Suggestion {
        return try await SuggestKit.createSuggestion(
            title: "Test Suggestion \(Date().timeIntervalSince1970)",
            description: "Created for testing purposes",
            userId: userId
        )
    }
    
    static func cleanup() async {
        // Clean up test data
        try? await SuggestKit.deleteTestData()
    }
}

Best Practices

Test Organization

// Organize tests by feature
class UserManagementTests: XCTestCase { }
class SuggestionTests: XCTestCase { }
class VotingTests: XCTestCase { }
class UITests: XCTestCase { }
class IntegrationTests: XCTestCase { }

Mock Data Best Practices

  • Realistic data: Use representative titles, descriptions, and user data
  • Edge cases: Test empty states, long text, special characters
  • Error scenarios: Simulate network failures, API errors
  • Performance: Test with large datasets

Continuous Testing

  • Run tests on every commit
  • Test on multiple iOS versions
  • Test different device sizes
  • Monitor test performance over time

Next Steps