Skip to content

Moroverse/test-kit

Repository files navigation

TestKit

A comprehensive Swift testing utilities library designed to simplify and enhance unit testing with Swift Testing framework.

Features

  • Memory Leak Detection: Track objects and verify proper deallocation
  • Asynchronous Testing: Control timing and verify state changes in async operations
  • UI Presentation Testing: Verify view controller presentation and dismissal
  • Sequential UUIDs: Generate predictable UUIDs for deterministic tests
  • Expectation Testing: Verify asynchronous operation results
  • Localization Testing: Validate localization strings exist

Installation

Add this package to your Swift package dependencies:

.package(url: "https://github.com/moroverse/test-kit.git", from: "0.3.1")

Then add the dependency to your target:

.target(
    name: "YourTarget",
    dependencies: ["TestKit"]
)

Usage Examples

Memory Leak Tracking

@Test("No memory leaks", .teardownTracking())
func testNoLeaks() async throws {
    let sut = MyViewModel()
    await Test.trackForMemoryLeaks(sut)
    // Test will fail if sut is not deallocated
}

AsyncSpy

@Test("Async operation succeeds")
func testAsyncSuccess() async throws {
    let spy = AsyncSpy<String>()
    let sut = MyViewModel(service: spy)
    
    try await spy.async {
        await sut.loadData()
    } expectationBeforeCompletion: {
        #expect(sut.isLoading == true)
    } completeWith: {
        .success("Result")
    } expectationAfterCompletion: { _ in
        #expect(sut.isLoading == false)
        #expect(sut.data == "Result")
    }
}

Expectation Tracking

    @Test("ViewModel fetches data successfully")
    func testFetchDataSuccess() async throws {
        let spy = MockNetworkService()
        let viewModel = ViewModel(networkService: spy)
        let data = ["item1", "item2"].joined(separator: ",").data(using: .utf8)!
        
        await Test.expect { try await viewModel.fetchItems() }
            .toCompleteWith { .success(["item1", "item2"]) }
            .when { await spy.completeWith(.success(data)) }
            .execute()
    }

    @Test("ViewModel handles fetch error")
    func testFetchDataError() async throws {
        let viewModel = ViewModel(networkService: FailingNetworkService())
        let error = NetworkError.connectionLost
        
        await Test.expect { try await viewModel.fetchItems() }
            .toCompleteWith { .failure(error) }
            .when { await spy.completeWith(.failure(error)) }
            .execute()
    }

UI Presentation Testing

@Test("Presents view controller", .serialized)
@MainActor
func testPresentation() async throws {
    let spy = PresentationSpy()
    let dummyVC = UIViewController()
    let presenter = UIViewController()
    
    presenter.present(dummyVC, animated: true)
    
    #expect(spy.presentations.count == 1)
    #expect(spy.presentations[0].controller === dummyVC)
    #expect(spy.presentations[0].state == .presented)
}

Sequential UUIDs

@Test("Sequential UUID generation", .sequentialUUIDGeneration())
func testSequentialUUIDs() async throws {
    await UUID.reset()
    
    let uuid1 = try await UUID.incrementing()
    #expect(uuid1.uuidString == "00000000-0000-0000-0000-000000000000")
    
    let uuid2 = try await UUID.incrementing()
    #expect(uuid2.uuidString == "00000000-0000-0000-0000-000000000001")
}

Requirements

  • iOS 17.0+, macOS 14.0+
  • Swift 6.1+
  • Xcode 15.0+

Dependencies

License

MIT License - See LICENSE.txt for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •