{"id":24589333,"url":"https://github.com/open-turo/swift-test-drs","last_synced_at":"2026-06-01T17:00:46.822Z","repository":{"id":272671308,"uuid":"681742029","full_name":"open-turo/swift-test-drs","owner":"open-turo","description":"Accelerate your testing velocity with Test DRS: Swift utilities for lightning-fast spying, stubbing, and mocking.","archived":false,"fork":false,"pushed_at":"2026-04-24T22:45:04.000Z","size":1381,"stargazers_count":10,"open_issues_count":9,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-04-25T00:33:59.555Z","etag":null,"topics":["macros","mock","mocking","spy","spying","stub","stubbing","swift","tdd","testing","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/open-turo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-22T16:55:03.000Z","updated_at":"2026-04-24T22:41:41.000Z","dependencies_parsed_at":"2025-01-16T00:02:45.109Z","dependency_job_id":"056f6a0d-a515-44c6-8577-9a230e5d0f86","html_url":"https://github.com/open-turo/swift-test-drs","commit_stats":null,"previous_names":["open-turo/swift-test-drs"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/open-turo/swift-test-drs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-turo%2Fswift-test-drs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-turo%2Fswift-test-drs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-turo%2Fswift-test-drs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-turo%2Fswift-test-drs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/open-turo","download_url":"https://codeload.github.com/open-turo/swift-test-drs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-turo%2Fswift-test-drs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33784631,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["macros","mock","mocking","spy","spying","stub","stubbing","swift","tdd","testing","unit-testing"],"created_at":"2025-01-24T08:15:11.649Z","updated_at":"2026-06-01T17:00:46.725Z","avatar_url":"https://github.com/open-turo.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TestDRS\n\n[![Swift 6.0+](https://img.shields.io/badge/Swift-6.0+-orange.svg)](https://swift.org)\n[![Platforms](https://img.shields.io/badge/Platforms-macOS%20|%20iOS%20|%20tvOS%20|%20watchOS%20|%20macCatalyst-blue.svg)](https://github.com/open-turo/swift-test-drs)\n[![License](https://img.shields.io/github/license/open-turo/swift-test-drs)](LICENSE)\n[![SwiftPM Compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg)](https://swift.org/package-manager/)\n\n**Test DRS** (Dependency Replacement System) is a modern Swift testing library that accelerates your testing velocity with lightning-fast spying, stubbing, and mocking capabilities. Built with Swift macros, TestDRS provides type-safe, compiler-verified test doubles that integrate seamlessly with both XCTest and Swift Testing frameworks.\n\n## Table of contents\n\n- [Features](#features)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [Core features](#core-features)\n  - [Creating mocks](#creating-mocks)\n  - [Stubbing methods](#stubbing-methods)\n  - [Verifying function calls](#verifying-function-calls)\n  - [Testing asynchronous code](#testing-asynchronous-code)\n  - [Testing static members](#testing-static-members)\n- [Testing patterns](#testing-patterns)\n- [Documentation](#documentation)\n- [Examples](#examples)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Features\n\n- ✨ **Zero Boilerplate**: Generate mocks with simple macro annotations\n- 🔒 **Type-Safe**: Compile-time verification of mock implementations\n- 🏃 **Swift Concurrency**: First-class support for async/await testing\n- 🎯 **Flexible**: Mock protocols, classes, and structs\n- 🧪 **Framework Agnostic**: Works with XCTest and Swift Testing\n- 📋 **Rich Verification**: Comprehensive call verification and parameter matching\n- ⚡ **Static Testing**: Isolated mocks of static members\n- 🔧 **Debugging**: Clear error messages and debug descriptions\n\n## Requirements\n\n- **Swift**: 6.0 or later\n- **Platforms**:\n  - macOS 13.0+\n  - iOS 16.0+\n  - tvOS 13.0+\n  - watchOS 6.0+\n  - macCatalyst 13.0+\n\n## Installation\n\n### Swift Package Manager\n\nAdd TestDRS to your project by adding the following dependency to your `Package.swift` file:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/open-turo/swift-test-drs\", from: \"X.X.X\")\n]\n```\n\nThen add TestDRS to your app target:\n\n```swift\n.target(\n    name: \"YourAppTarget\",\n    dependencies: [\"TestDRS\"]\n)\n```\n\n\u003e **⚠️ Important ⚠️**: Only link TestDRS to your app target or your test target, never to both! When linking to your app target, your test target will automatically have transitive access to TestDRS through your app target. This prevents duplicate symbol issues that can cause runtime problems. Most users will want to link TestDRS to their app target as that allows for using generated mocks in SwiftUI previews.\n\n### Xcode\n\n1. In Xcode, go to **File → Add Package Dependencies**\n2. Enter the repository URL: `https://github.com/open-turo/swift-test-drs`\n3. Select your desired version\n4. Add TestDRS to your app target (not your test target)\n\n## Quick start\n\nHere's a simple example to get you started with TestDRS:\n\n```swift\nimport TestDRS\nimport XCTest\n\n// 1. Add the @AddMock macro to generate a mock\n@AddMock\nprotocol WeatherService {\n    func fetchWeather(for city: String) async throws -\u003e Weather\n}\n\n// 2. Use the generated mock in your tests\nfinal class WeatherViewModelTests: XCTestCase {\n    func testFetchWeather() async throws {\n        // Create mock instance\n        let mockWeatherService = MockWeatherService()\n        let viewModel = WeatherViewModel(weatherService: mockWeatherService)\n\n        // Stub method behavior\n        let expectedWeather = Weather(temperature: 72, condition: .sunny)\n        #stub(mockWeatherService.fetchWeather, returning: expectedWeather)\n\n        // Execute code under test\n        try await viewModel.loadWeather(for: \"San Francisco\")\n\n        // Verify results and interactions\n        XCTAssertEqual(viewModel.currentWeather, expectedWeather)\n        #expectWasCalled(mockWeatherService.fetchWeather, with: \"San Francisco\")\n            .exactlyOnce()\n    }\n}\n```\n\n## Core features\n\n### Creating mocks\n\nTestDRS offers two primary approaches for creating mocks:\n\n#### `@AddMock` - Recommended Approach\n\nApply `@AddMock` to your production types to automatically generate mock implementations:\n\n```swift\n@AddMock\nprotocol NetworkService {\n    var timeout: TimeInterval { get set }\n    func fetchData(from url: URL) async throws -\u003e Data\n}\n\n@AddMock\nclass DatabaseManager {\n    func save(_ data: Data) throws\n    func fetch(id: String) -\u003e Data?\n}\n```\n\n**Benefits:**\n- Keeps mocks in sync with production code\n- Available in debug builds for SwiftUI previews\n- Zero maintenance overhead\n\n#### `@Mock` - Manual Control\n\nCreate dedicated mock types in your test target:\n\n```swift\n@Mock\nclass MockNetworkService: NetworkService {\n    var timeout: TimeInterval\n    func fetchData(from url: URL) async throws -\u003e Data\n}\n```\n\n**Benefits:**\n- Complete control over mock location\n- Not included in production target\n- Explicit mock definitions\n\n### Stubbing methods\n\nControl mock behavior with powerful stubbing capabilities:\n\n#### Return values\n\n```swift\n#stub(mockService.fetchData, returning: expectedData)\n#stub(mockService.timeout, returning: 30.0)\n```\n\n#### Throwing errors\n\n```swift\n#stub(mockService.fetchData, throwing: NetworkError.connectionFailed)\n```\n\n#### Dynamic behavior with closures\n\n```swift\n#stub(mockService.fetchData, using: { url in\n    if url.host == \"api.example.com\" {\n        return mockData\n    } else {\n        throw NetworkError.invalidURL\n    }\n})\n```\n\n### Verifying function calls\n\nComprehensive verification of mock interactions:\n\n#### Basic verification\n\n```swift\n#expectWasCalled(mockService.fetchData)\n#expectWasNotCalled(mockService.deleteData)\n```\n\n#### Parameter verification\n\n```swift\n#expectWasCalled(mockService.fetchData, with: expectedURL)\n#expectWasCalled(mockService.logEvent, with: \"user_login\", [\"id\": \"123\"], 1)\n```\n\n#### Call count verification\n\n```swift\n// Exactly once\n#expectWasCalled(mockService.fetchData).exactlyOnce()\n\n// Specific count\n#expectWasCalled(mockService.retry).occurring(times: 3)\n\n// Range\n#expectWasCalled(mockService.poll).occurringWithin(times: 2...5)\n```\n\n#### Advanced parameter inspection\n\nFor non-Equatable parameters, access the actual call data:\n\n```swift\nlet result = #expectWasCalled(mockService.processComplexData).exactlyOnce()\nlet call = try result.getMatchingCall()\nlet (data, options) = call.input\n\nXCTAssertEqual(data.id, \"expected-id\")\nXCTAssertTrue(options.preserveMetadata)\n```\n\n### Testing asynchronous code\n\nTestDRS provides first-class support for async/await testing:\n\n#### Direct awaiting (recommended)\n\n```swift\nfunc testAsyncOperation() async throws {\n    let mockService = MockAsyncService()\n    #stub(mockService.fetchData, returning: expectedData)\n    systemUnderTest.service = mockService\n\n    let result = try await systemUnderTest.performOperation()\n\n    #expectWasCalled(mockService.fetchData)\n    XCTAssertEqual(result, expectedResult)\n}\n```\n\n#### Confirmation of calls\n\nFor scenarios where you can't directly await operations:\n\n```swift\nfunc testNotificationHandler() async {\n    let mockService = MockService()\n    let handler = NotificationHandler(service: mockService)\n\n    // Trigger notification\n    NotificationCenter.default.post(name: .dataUpdated, object: nil)\n\n    // Wait for async call triggered by notification\n    await #confirmationOfCall(to: mockService.processUpdate)\n        .exactlyOnce()\n}\n```\n\n### Testing static members\n\nTest code that uses static methods and properties with proper isolation:\n\n```swift\n@AddMock\nclass Logger {\n    static func log(_ message: String, level: LogLevel = .info)\n    static var isEnabled: Bool\n}\n\nfunc testLogging() {\n    withStaticTestingContext {\n        // Configure mock behavior\n        #stub(TestDRSMockLogger.isEnabled, returning: true)\n        systemUnderTest.logger = TestDRSMockLogger.self\n\n        // Test code that uses Logger.log(...)\n        systemUnderTest.performAction()\n\n        // Verify static method calls\n        #expectWasCalled(TestDRSMockLogger.log, with: \"Action completed\", .info)\n    }\n}\n```\n\n## Testing patterns\n\n### Mock closure testing\n\nTest callbacks and completion handlers:\n\n```swift\n@Mock\nstruct CallbackHandler {\n    func onComplete(result: Result\u003cData, Error\u003e)\n}\n\nfunc testCallback() {\n    let mockHandler = CallbackHandler()\n\n    dataLoader.loadData(completion: mockHandler.onComplete)\n\n    #expectWasCalled(mockHandler.onComplete)\n        .exactlyOnce()\n}\n```\n\n### Framework compatibility\n\nWorks seamlessly with both testing frameworks:\n\n#### XCTest\n\n```swift\nimport XCTest\nimport TestDRS\n\nfinal class MyTests: XCTestCase {\n    func testExample() throws {\n        // TestDRS code here\n    }\n}\n```\n\n#### Swift Testing\n\n```swift\nimport Testing\nimport TestDRS\n\nstruct MyTests {\n    @Test func example() throws {\n        // TestDRS code here\n    }\n}\n```\n\n## Documentation\n\n📚 **[Complete Documentation](https://open-turo.github.io/swift-test-drs/documentation/testdrs/)**\n\n- [Getting Started Guide](https://open-turo.github.io/swift-test-drs/documentation/testdrs/gettingstarted)\n- [Testing Asynchronous Code](https://open-turo.github.io/swift-test-drs/documentation/testdrs/testingasynchronouscode)\n- [Mocking Static Members](https://open-turo.github.io/swift-test-drs/documentation/testdrs/mockingstaticmembers)\n- [FAQ](https://open-turo.github.io/swift-test-drs/documentation/testdrs/faq)\n\n## Examples\n\nThe repository includes a small example project demonstrating TestDRS usage:\n\n- **[ExampleClient](ExampleClient/)**\n\n## Contributing\n\nWe welcome contributions to TestDRS! Whether you're fixing bugs, adding features, improving documentation, or helping with issues, your contributions are valued.\n\n### Ways to contribute\n\n- **Report bugs** by opening GitHub issues\n- **Request features** or suggest improvements\n- **Submit code** via pull requests\n- **Improve documentation** and examples\n- **Help others** by answering questions in issues\n\n### Before contributing\n\nFor larger changes, please open an issue first to discuss your approach. This helps ensure your contribution aligns with the project's direction and prevents duplicate work.\n\n### Development setup\n\n1. **Fork** the repository on GitHub\n2. **Clone** your fork locally:\n   ```bash\n   git clone https://github.com/YOUR_USERNAME/swift-test-drs.git\n   cd swift-test-drs\n   ```\n3. **Open** the package in Xcode:\n   ```bash\n   open Package.swift\n   ```\n\n### Code standards\n\n- **Swift style**: Follow standard Swift conventions\n- **Testing**: Add tests for new features and bug fixes\n- **Documentation**: Document public APIs with DocC comments\n- **Compatibility**: Ensure changes work across supported platforms\n\n### Testing\n\n```bash\n# Run all tests\nswift test\n\n# Run specific test target\nswift test --filter TestDRSTests\n\n# Run macro tests\nswift test --filter TestDRSMacrosTests\n```\n\n### Pull request process\n\n1. **Create** a feature branch (`git checkout -b feature/your-feature`)\n2. **Make** your changes with tests\n3. **Ensure** all tests pass locally\n4. **Commit** with clear, descriptive messages\n5. **Push** to your fork (`git push origin feature/your-feature`)\n6. **Open** a pull request with:\n   - Clear description of changes\n   - Link to related issues\n\n### Building documentation\n\n```bash\n# Generate DocC documentation\n./Scripts/build_docc.sh\n```\n\n## License\n\nTestDRS is available under the MIT license. See the [LICENSE](LICENSE) file for more info.\n\n---\n\n**Maintained by [Turo Open Source](https://github.com/open-turo)**\n\n*Accelerate your testing velocity with TestDRS* 🚀\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-turo%2Fswift-test-drs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-turo%2Fswift-test-drs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-turo%2Fswift-test-drs/lists"}