{"id":16224540,"url":"https://github.com/kateinoigakukun/stubkit","last_synced_at":"2025-04-06T10:13:58.472Z","repository":{"id":42684864,"uuid":"163555268","full_name":"kateinoigakukun/StubKit","owner":"kateinoigakukun","description":"A smart stubbing system.","archived":false,"fork":false,"pushed_at":"2024-08-07T17:49:45.000Z","size":193,"stargazers_count":299,"open_issues_count":0,"forks_count":10,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-10-11T12:24:27.478Z","etag":null,"topics":["swift","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/kateinoigakukun.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-30T02:18:10.000Z","updated_at":"2024-10-01T09:55:59.000Z","dependencies_parsed_at":"2024-10-25T17:23:35.476Z","dependency_job_id":"6eab58d2-e837-45b0-8d55-6d2433d6176c","html_url":"https://github.com/kateinoigakukun/StubKit","commit_stats":{"total_commits":62,"total_committers":3,"mean_commits":"20.666666666666668","dds":"0.032258064516129004","last_synced_commit":"39ed8a3099a7ea5cba87be394d02254d293af3bc"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kateinoigakukun%2FStubKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kateinoigakukun%2FStubKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kateinoigakukun%2FStubKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kateinoigakukun%2FStubKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kateinoigakukun","download_url":"https://codeload.github.com/kateinoigakukun/StubKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247464225,"owners_count":20942970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["swift","testing"],"created_at":"2024-10-10T12:24:39.697Z","updated_at":"2025-04-06T10:13:58.444Z","avatar_url":"https://github.com/kateinoigakukun.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# StubKit\n\n![Run unit tests](https://github.com/kateinoigakukun/StubKit/workflows/Run%20unit%20tests/badge.svg) [![codecov](https://codecov.io/gh/kateinoigakukun/StubKit/branch/main/graph/badge.svg?token=WqSnBYoNJq)](https://codecov.io/gh/kateinoigakukun/StubKit)\n\n![](https://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20Linux%20%7C%20WebAssembly-blue)\n\nA smart stubbing system.\n\n## Installation\n\n### Using Swift Package Manager\n\n```\n.package(url: \"https://github.com/kateinoigakukun/StubKit.git\", from: \"0.1.0\"),\n```\n\n### Using [Carthage](https://github.com/Carthage/Carthage)\n\n```\ngithub \"kateinoigakukun/StubKit\"\n```\n\n### Using [CocoaPods](https://cocoapods.org/)\n\n```\npod \"SwiftStubKit\"\n```\n\n## Usage\n\n### Getting started\n\nYou can instantiate any kind of `Decodable` with a single line.\n\n```swift\nimport StubKit\n\n// Codable struct\nstruct User: Codable {\n  let id: Int\n  let name: String\n  let sex: Sex\n}\n\nlet stubUser = try Stub.make(User.self)\n// User(id: 1234, name: \"This is Stub String\", sex: .female)\n```\n\n### Customize property\n\nYou can customize properties even if the property is defined as `let`.\n```swift\nlet maleUser = try Stub.make(User.self) {\n  $0.set(\\.sex, value: .male)\n}\n// User(id: 1234, name: \"This is Stub String\", sex: .male)\n```\n\n### Using `Stubbable`\n\nIf you want to customize the default stub value, please conform `Stubbable`.\n```swift\nextension String: Stubbable {\n  static func stub() -\u003e String {\n    return \"This is custommized Stub String\"\n  }\n}\n\nlet stubUser = try Stub.make(User.self)\n// User(id: 1234, name: \"This is customized Stub String\", sex: .female)\n```\n\n### Advanced Usage\n\n```swift\n\nstruct RandomIntStubProvider: StubProvider {\n    func stub\u003cT\u003e(of type: T.Type) -\u003e T? {\n        if type is Int.Type {\n            return Int.random(in: 0..\u003c100) as? T\n        }\n        return nil\n    }\n}\n\nlet userStub = Stub(type: User.self, provider: [RandomIntStubProvider()])\ntry userStub.make() // User(id: 97)\ntry userStub.make() // User(id: 54)\ntry userStub.make() // User(id: 12)\n```\n\n### Need to conform non-final class as Stubbable?\n\nYou can make it `Stubbable` by defining the `UnsafeStubbable`.\n\n```swift\npublic protocol UnsafeStubbable: Stubbable {\n    associatedtype Target = Self\n    static func unsafeStub() -\u003e Target\n}\n\nextension UnsafeStubbable {\n    public static func stub() -\u003e Self {\n        return unsafeStub() as! Self\n    }\n}\n\nextension UIImage: UnsafeStubbable {\n    public static func unsafeStub() -\u003e UIImage {\n        return #imageLiteral(resourceName: \"dummy\")\n    }\n}\n```\n\n\n## How does it work\n\nStubKit mainly uses two techniques.\n- Traverse using `Decoder` protocol.\n- Inject value with non-mutable `KeyPath`.\n- Existential type using `Self`.\n\n### Traverse struct using `Decoder` protocol\n![](./resources/tree.png)\n\nSwift has `Decodable` protocol and if a type conforms to `Decodable`, Swift compiler generates some code to decode internally. So we can decode a JSON to Swift struct without any configuration. StubKit uses this system to construct instance through `Decoder`. `Decoder` is a protocol which provide a value by key or index like `JSONDecoder`. If we pass the `Decoder` which just provide a stub value recursively, we can instantiate any kind of `Decodable` instance.\n\n\n### Inject value with non-mutable `KeyPath`\n\nI know it's only natural but, Swift can't mutate `let` defined property. But Swift has `MemoryLayout\u003cT\u003e.offset` which provide the offset to the property from its own address. So actually in memory we can mutate `let` property.\n\n\n### Existential type using `Self`\n\nA protocol that has `associatedtype` or uses `Self` type can't be existential type. But using `Self` for return type of method is only available in Swift4.2. (I think using `Self` for type of getter should be also available.) This technique makes `Stubbale` type-safely.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkateinoigakukun%2Fstubkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkateinoigakukun%2Fstubkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkateinoigakukun%2Fstubkit/lists"}