Testing Strategies
Unit Testing with Mocks
SuggestKit provides mock implementations for testing:Copy
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:Copy
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
Copy
#if DEBUG
extension SuggestKit {
static func setupForTesting() {
configureMock(
mockSuggestions: true,
mockUsers: true,
mockVotes: true,
mockNetworkDelay: 0.1,
simulateOfflineMode: false
)
}
}
#endif
Custom Mock Data
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
#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
Copy
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
Copy
# .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
Copy
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
Copy
// 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