Testing strategy and coverage for GitHub Repository Search iOS app.
Current Status:
- Total: 8 test cases (all implemented and passing!)
- Unit Tests: 4 tests (3 API tests + 1 performance test) - all passing
- UI Tests: 4 tests (3 UI interaction tests + 1 launch performance) - all passing
- Success Rate: 8/8 (100%)
- Code Coverage: 51.87% (Improved from 26.68%!)
- ApiClient: 88.89%
- HomeView: 87.63% (up from 68.87%)
- AppSearchBar: 100% (up from 90.48%)
- SearchItem: 100% (up from 0%)
- Integration Tests: Not explicitly separated
| Test Type | Framework | What It Verifies | Our Coverage |
|---|---|---|---|
| Unit | XCTest | One function or class, isolated | 4 tests |
| Integration | XCTest | Multiple components together | 0 tests |
| UI | XCTest (XCUI) | Real user flows on screen | 3 tests |
| Performance | XCTest (Metrics) | Speed and memory over time | 1 test |
See TEST_ORGANIZATION.md for detailed folder structure recommendations.
According to Apple's official documentation, iOS apps should have:
Status: Implemented
Location: github_repo_search_iOS_appTests/
Purpose: Test individual functions, methods, and classes in isolation
Current Coverage:
- API networking (
ApiClient.swift) - Repository pattern (
GithubRepository.swift) - Request building (
SearchRepoRequest.swift) - Error handling (
ApiResponseError.swift)
Example: GitRepository_appTests.swift - Tests API calls and data parsing
Status: Partially Implemented
Location: github_repo_search_iOS_appUITests/
Purpose: Test user interface interactions and workflows
Current Coverage:
- Launch test (
testHomeHeadView) - implemented - Search field test - stub only
- Search results test - stub only
Example: HomeView_appUITests.swift - Tests UI elements exist
Status: Implemented
Location: github_repo_search_iOS_appUITests/github_repo_search_iOS_appUITests.swift
Purpose: Measure app performance metrics
Current Coverage:
- Launch time measurement (
testLaunchPerformance)
Status: Not explicitly separated Purpose: Test how multiple components work together
Recommendation: Consider adding tests for:
- View + ViewModel integration
- API + Repository + ViewModel flow
- Navigation flows
Location: github_repo_search_iOS_appTests/
1. testGetSearchResultInRepoNameSomeData
- Location:
GitRepository_appTests.swift - Tests: Successful API calls with valid query
- Covers:
GithubRepository,ApiClient,SearchRepoRequest, JSON parsing - Expected: Returns results with count > 0
2. testGetSearchResultInRepoNameEmptyResult
- Location:
GitRepository_appTests.swift - Tests: Searches returning no results
- Covers: Empty result handling, edge cases
- Expected: Returns empty or handles gracefully
3. testGetSearchResultInRepoNameInvalidQuery
- Location:
GitRepository_appTests.swift - Tests: Invalid requests (empty query)
- Covers: Error handling, validation errors
- Expected: Throws validation error
4. testPerformanceExample
- Location:
github_repo_search_iOS_appTests.swift - Tests: Performance measurement (stub - empty)
- Status: Placeholder for future performance tests
Location: github_repo_search_iOS_appUITests/
1. testHomeHeadView
- Location:
HomeView_appUITests.swift - Tests: Home screen UI elements after splash
- Checks: Navigation bar, search field, header text
- Status: Implemented and passing
2. testHomeViewBodySearchField
- Location:
HomeView_appUITests.swift - Tests: Search field interaction
- Checks: Field exists, accepts input, text can be typed and cleared
- Status: Implemented and passing
3. testHomeViewBodySearchResult
- Location:
HomeView_appUITests.swift - Tests: Search results display after typing query
- Checks: Results appear or appropriate message shown (loading/empty/error)
- Status: Implemented and passing
4. testLaunchPerformance
- Location:
github_repo_search_iOS_appUITests.swift - Tests: App launch time performance
- Measures: XCTApplicationLaunchMetric
- Status: Implemented and passing
- XCTest framework with async/await
- @MainActor for thread safety
- All tests use modern Swift concurrency
Files Tested:
ApiClient.swift- Networking layerGithubRepository.swift- Repository patternSearchRepoRequest.swift- API requestsApiResponseError.swift- Error handling
Not Yet Covered:
- UI Views, View Models, Navigation
Bug Fixes:
- Fixed
GithubRepository.swiftto useperPageparameter instead of hardcoded constant - Updated test query from "swiftmvvmin:name" to "swift" for more reliable results
- Fixed UI test
testHomeHeadViewto wait for splash screen and check correct UI elements - All API tests (3/3) now passing consistently
New Tests Implemented:
testHomeViewBodySearchField- Tests search field interaction, typing, and clearingtestHomeViewBodySearchResult- Tests search results display after query- Both UI test stubs now fully implemented with assertions
Coverage Improvements:
- Overall: 26.68% → 51.87% (nearly doubled!)
- HomeView: 68.87% → 87.63%
- AppSearchBar: 90.48% → 100%
- SearchItem: 0% → 100%
Status: All 8 tests passing (100% success rate), 51.87% code coverage
Measures % of code executed during tests. Helps find untested areas.
Important Philosophy:
100% code coverage ≠ 100% test coverage
A line being executed doesn't mean its behavior was asserted. Code coverage shows which lines ran, not whether they were properly validated.
- Networking & API: 90-100%
- Business Logic: 80-90%
- Overall: 70%+
Xcode: Edit Scheme (⌘<) → Test → Options → Check "Code Coverage"
- Press ⌘9 (Report Navigator)
- Select test run
- Click "Coverage" tab
- See file percentages (green = good, red = needs tests)
Generate coverage reports from command line:
# View help
xcrun xccov --help
man xccov
# Run tests with coverage
xcodebuild test -scheme github_repo_search_iOS_app \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.6' \
-enableCodeCoverage YES
# After running tests with coverage enabled
xcrun xccov view --report /path/to/test.xcresult
# Example output shows file-by-file coverage percentages
# github_repo_search_iOS_app.app: 51.87% (1110/2140)
# ApiClient.swift: 88.89% (32/36)
# HomeView.swift: 87.63% (425/485)
# AppSearchBar.swift: 100.00% (42/42)
# SearchItem.swift: 100.00% (6/6)
# SplashView.swift: 95.89% (70/73)Note: xccov has no web documentation - use man xccov for the official reference.
- All tests: Press ⌘U
- One test: Click diamond icon
- Results: ⌘6 (Test Navigator) or ⌘9 (Report Navigator)
# All tests
xcodebuild test -scheme github_repo_search_iOS_app \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.6'
# With coverage
xcodebuild test -scheme github_repo_search_iOS_app \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.6' \
-enableCodeCoverage YES
# Specific test class
xcodebuild test -scheme github_repo_search_iOS_app \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.6' \
-only-testing:github_repo_search_iOS_appTests/GithubRepository_appTests
# List available simulators
xcrun simctl list devices available- Test independence (no shared state)
- Descriptive test names
- Async/await (no callbacks)
- Arrange-Act-Assert pattern
Tests not running? Clean build (⌘⇧K) and rebuild (⌘B)
Coverage not showing? Enable in scheme, re-run tests
Tests timeout? Check network or async/await usage
- Open Xcode
- Press ⌘U (run tests)
- Press ⌘9 → Coverage tab (view results)
- Done!
Based on Apple's testing best practices:
-
Complete UI Tests
- Test search field input
- Test search results display
- Test navigation flows
- Test error states
-
ViewModel Tests
- Test HomeViewModel logic
- Test state changes
- Test error handling
-
Integration Tests
- Test complete user flows
- Test API → Repository → ViewModel → View
-
Snapshot Tests (Optional)
- Visual regression testing
- UI consistency across devices
Swift Testing is Apple's modern testing framework introduced in 2024 as the recommended default for new tests.
Key Differences from XCTest:
| Feature | XCTest | Swift Testing |
|---|---|---|
| Syntax | XCTAssertEqual |
#expect |
| Parallel | Serial by default | Parallel by default |
| Parameters | Manual loops | @Test(arguments:) macro |
| Async/await | Supported but verbose | Native, clean support |
| UI Tests | ✓ Supported (XCUI) | ✗ XCTest only |
| Performance | ✓ Supported (Metrics) | ✗ XCTest only |
Old (XCTest):
import XCTest
@testable import MyApp
class CalculatorTests: XCTestCase {
func testAddition() {
let calc = Calculator()
let result = calc.add(2, 3)
XCTAssertEqual(result, 5)
}
}New (Swift Testing):
import Testing
@testable import MyApp
struct CalculatorTests {
@Test func addition() {
let calc = Calculator()
let result = calc.add(2, 3)
#expect(result == 5)
}
}- Leave existing XCTests alone - They still run and count
- Write all new tests in Swift Testing
- Migrate files you touch most, at your own pace
- UI and Performance tests stay in XCTest - No migration needed
- Both frameworks coexist - Run side by side in same target
- Now: If targeting iOS 18+ and starting new tests
- Later: If supporting iOS 17 or below (Swift Testing requires iOS 18+)
- Never: For UI tests and performance tests (XCTest is the only option)
Current Project Status: Using XCTest for all tests (iOS 17+ compatibility)
Priority #1 - Code Coverage:
- Determining how much code your tests cover - THE official coverage guide (enabling in scheme, reading reports, using coverage to guide test development)
Testing Hub: 2. Test coverage / Testing topic hub - Parent section covering logic failures, UI problems, performance regressions
Testing Frameworks: 3. XCTest framework reference - Traditional iOS testing framework
- Swift Testing framework reference - Modern Swift-native testing (iOS 18+)
Command Line Tools:
5. xccov CLI - No web page exists; run man xccov or xcrun xccov --help in Terminal for the official reference
6. xcodebuild CLI - No web page exists; run man xcodebuild or xcodebuild -help for the official reference
Additional Resources:
- Testing Your Apps in Xcode - Complete Xcode testing guide
- Running Tests and Interpreting Results - How to run and read test results
- Defining Test Cases and Test Methods - How to write XCTest tests
1. Executed ≠ Verified
A covered line means it ran — not that its behavior was checked. Example:
func testProcessOrder() {
let service = OrderService()
_ = service.process(Order(itemID: "sku-42"))
// No assertion. Nothing verified.
}Every line in process() counts as covered, but if it returns garbage, this test still passes.
2. Coverage is a Flashlight, Not a Scoreboard
Apple's official guidance: Coverage exists to find untested paths and guide new test development. It's not a metric to maximize.
- 100% code coverage ≠ 100% test coverage
- Coverage measures execution; assertions measure correctness
- Use it to find red (untested) code, not to chase a percentage
3. Assertion Quality Matters More Than Percentage
A file at 85% with sharp assertions beats a file at 100% with none.