Layover

Testing Guide

Running Tests

All Tests

swift test

Specific Test Suite

swift test --filter UserTests
swift test --filter RoomTests
swift test --filter RoomServiceTests

With Verbose Output

swift test --verbose

Test Structure

Model Tests (Tests/Models/)

Service Tests (Tests/Services/)

ViewModel Tests (Tests/ViewModels/)

Test Coverage

Current coverage areas:

Models

Services

ViewModels

Writing New Tests

Example Test

import Testing
@testable import Layover

@Suite("My Feature Tests")
struct MyFeatureTests {
    
    @Test("Test description")
    func testSomething() async throws {
        // Arrange
        let service = MyService()
        
        // Act
        let result = try await service.doSomething()
        
        // Assert
        #expect(result.isValid)
    }
}

Testing Async Code

@Test("Async operation")
func testAsync() async throws {
    let viewModel = MyViewModel()
    await viewModel.loadData()
    #expect(viewModel.data.isEmpty == false)
}

Testing Errors

@Test("Error handling")
func testError() async {
    let service = MyService()
    await #expect(throws: MyError.self) {
        try await service.failingOperation()
    }
}

CI/CD Integration

GitHub Actions Example

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v3
      - name: Run tests
        run: swift test

Xcode Cloud

Configure in App Store Connect with:

Best Practices

  1. Test Isolation - Each test should be independent
  2. Arrange-Act-Assert - Clear test structure
  3. Meaningful Names - Descriptive test function names
  4. Fast Tests - Mock external dependencies
  5. Comprehensive Coverage - Test edge cases
  6. @MainActor - Use for ViewModels and Services that need it

Mocking

Example Mock Service

final class MockRoomService: RoomServiceProtocol {
    var rooms: [Room] = []
    var createRoomCalled = false
    
    func createRoom(name: String, hostID: UUID, activityType: RoomActivityType) async throws -> Room {
        createRoomCalled = true
        let room = Room(name: name, hostID: hostID, activityType: activityType)
        rooms.append(room)
        return room
    }
    
    // Implement other protocol methods...
}

Using Mocks in Tests

@Test("ViewModel uses service")
func testViewModelWithMock() async {
    let mockService = MockRoomService()
    let viewModel = RoomListViewModel(roomService: mockService)
    
    await viewModel.createRoom(name: "Test", hostID: UUID(), activityType: .appleTVPlus)
    
    #expect(mockService.createRoomCalled)
}

Performance Testing

Example

@Test("Performance test")
func testPerformance() async throws {
    let service = TexasHoldemService()
    let roomID = UUID()
    let players = Array(repeating: UUID(), count: 10)
    
    // Measure time
    let start = Date()
    _ = try await service.startGame(roomID: roomID, players: players)
    let duration = Date().timeIntervalSince(start)
    
    #expect(duration < 1.0) // Should complete in under 1 second
}

Debug Tips

@Test("Debug test")
func testWithDebug() {
    let value = calculateSomething()
    print("Debug value: \(value)")
    #expect(value > 0)
}

Conditional Tests

@Test("iOS only test")
@available(iOS 17.0, *)
func testIOSFeature() {
    // iOS-specific test
}

Test Data Helpers

Example Factory

extension Room {
    static func testRoom(activityType: RoomActivityType = .appleTVPlus) -> Room {
        Room(
            name: "Test Room",
            hostID: UUID(),
            activityType: activityType
        )
    }
}

// Usage in tests
@Test("Using test factory")
func testWithFactory() {
    let room = Room.testRoom(activityType: .texasHoldem)
    #expect(room.activityType == .texasHoldem)
}

Continuous Improvement

Regular test maintenance: