{"id":23131227,"url":"https://github.com/emartech/mimic","last_synced_at":"2025-07-27T12:09:00.656Z","repository":{"id":95429231,"uuid":"606056199","full_name":"emartech/mimic","owner":"emartech","description":"faking package for Swift","archived":false,"fork":false,"pushed_at":"2024-04-12T11:21:07.000Z","size":172,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-04-14T07:41:04.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://emartech.github.io/mimic/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emartech.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}},"created_at":"2023-02-24T13:56:29.000Z","updated_at":"2024-04-15T12:25:10.729Z","dependencies_parsed_at":null,"dependency_job_id":"4f8e7bb9-ee95-4bda-8e97-61d6aaa3765c","html_url":"https://github.com/emartech/mimic","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emartech%2Fmimic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emartech%2Fmimic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emartech%2Fmimic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emartech%2Fmimic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emartech","download_url":"https://codeload.github.com/emartech/mimic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247137669,"owners_count":20889909,"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":[],"created_at":"2024-12-17T11:12:21.590Z","updated_at":"2025-04-04T07:27:37.222Z","avatar_url":"https://github.com/emartech.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2023 Emarsys-Technologies Kft.\n\nSPDX-License-Identifier: Apache-2.0\n--\u003e\n\n![Last commit build](https://github.com/emartech/mimic/actions/workflows/on_commit.yml/badge.svg)\n\n\u003cimg src=\"assets/mimic.png\" alt=\"Mimic\" width=\"100\" height=\"100\"\u003e\n\n## What is mimic?\n\n`Mimic` is a testing framework designed specifically for the `Swift` programming language. It offers two key features that simplify the testing process. \n- Firstly, it provides a convenient way to create fake objects, which can be used similarly as a mock object.\n- Secondly, it could generate instances for codable classes.\n\nSince `Swift's` reflection is limited and rather we call it to introspection, creating runtime mocking frameworks are hardly possible. Most of the solutions are working with build time mock generations where creating mocks are marked with protocols or comments. This solution was created because we felt these solutions a bit unconvinient or not clean enough from a software development point of view.\nOf course perfect solution doesn't exist, and this is also just a [leaky abstraction ](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/), the trade of here is that we have to create fake classes manually but this package helps us to make this painful method as convinient as possible. Moreover to be able to replace the behaviour of a class it forces the developer to organize the code in a clean way.\n\n## Faking\n\nBy using fake objects, you can simulate various scenarios and ensure that your code behaves as expected in different situations by isolating the behaviors of specific components during testing. `Mimic` provides a simple and intuitive API that allows you to specify the expected behavior and responses of the fake object. This way, you can precisely control the interactions between your tests and the fake object, making testing more reliable and reproducible.\n\n### Creating fake objects\n\nLets say we have a protocol which defines a function.\n\n```swift\nprotocol SomeProtocol {\n    \n    func someFunction(with parameter: String) -\u003e Int\n    \n}\n```\n\nAnd we have a class which conforms to this protocol and adds some implementation for that.\n\n```swift\nclass SomeClass: SomeProtocol {\n    \n    func someFunction(with parameter: String) -\u003e Int {\n        return 42\n    }\n    \n}\n```\n\nSo now comes the tricky part, to create a fake object that could be used from tests for `SomeClass` class, we should do this:\n\n```swift\nfinal class FakeSomeClass: SomeProtocol, Mimic {\n    \n    let fnSomeFunction = Fn\u003cInt\u003e()\n    \n    func someFunction(with parameter: String) -\u003e Int {\n        return try! fnSomeFunction.invoke(params: parameter)\n    }\n    \n}\n```\n#### Key steps: \n- add `Mimic` protocol to the fake class\n- create a `Fn` instance where the generic type is the result type of the function that behaviour we want to replace\n- add the fn instance to the function and invoke, pass the parameters and return with it\n\nThat's it. So as it is visible from the example above, it needs some code typing, but not so terrible, no need to add internal logic, or enything else, just a pure declaration, and teachings and verifications will work.\n\n### Using fake objects\n  \n#### Checking properties\n\nIf we have a fake class that has properties, we can easily check their values by creating an expected dictionary where the key is the name of the property and the values are what we expecting, than make Props object from both and check their equality.\n\n```swift\n    let expected = [\n        \"boolValue\": true,\n        \"wholeValue\": 42,\n        \"floatingValue\": 3.1415926535897,\n        \"stringValue\": \"Oh, yeah. Oooh, ahhh, that's how it always starts. Then later there's running and um, screaming.\",\n        \"array\": [true, false, true],\n        \"dict\": [\"key\": \"value\"],\n        \"optionalNil\": nil\n    ].toProps()\n    \n    let result = testStruct.props()\n    \n    XCTAssertEqual(result, expected)\n```\n\n### `when` used for teaching / stubbing\n\n#### `replaceFunction`\nSometimes we need more control on the function.\n```swift\n    let expected = 496\n    \n    var parameter: String!\n    var times = 0\n\n    fakeSomeClass.when(\\.fnSomeFunction).replaceFunction { invocationCount, params in\n        times = invocationCount\n        parameter = params[0]\n        return expected\n    }\n    \n    let result = testStruct.someFunction(with: \"parameterValue\")\n    \n    XCTAssertEqual(result, expected)\n    XCTAssertEqual(parameter, \"parameterValue\")\n    XCTAssertEqual(times, 1)\n```\n#### Key steps: \n- with calling `when` function on the faked class and passing the `Fn` functions `keyPath` we can use the `replaceFunction`\n- setting the explicit type for a parameter before getting it with index from `params` is mandatory\n- `invocationCount` can be used to check how many times were the function called\n\n#### `thenReturn`\nSometimes we just want to stub it and return with a simple value.\n```swift\n    let expected = 496\n\n    fakeSomeClass.when(\\.fnSomeFunction).thenReturn(expected)\n    \n    let result = testStruct.someFunction(with: \"parameterValue\")\n    \n    XCTAssertEqual(result, expected)\n```\n#### Key steps: \n- with calling `when` function on the faked class and passing the `Fn` functions `keyPath` we can use the `thenReturn`\n- as a parameter of `thenReturn` function we can pass the return value\n\n#### `thenReturns`\nSometimes we just want to stub it and return with a simple value.\n```swift\n    let expected1 = 6\n    let expected2 = 28\n    let expected3 = 496\n    let expected5 = 8128\n\n    fakeSomeClass.when(\\.fnSomeFunction).thenReturns(expected1, expected2 , expected3, nil, expected4)\n    \n    let result1 = testStruct.someFunction(with: \"parameterValue\")\n    let result2 = testStruct.someFunction(with: \"parameterValue\")\n    let result3 = testStruct.someFunction(with: \"parameterValue\")\n    let result4 = testStruct.someFunction(with: \"parameterValue\")\n    let result5 = testStruct.someFunction(with: \"parameterValue\")\n    \n    XCTAssertEqual(result1, expected1)\n    XCTAssertEqual(result2, expected2)\n    XCTAssertEqual(result3, expected3)\n    XCTAssertNil(result4)\n    XCTAssertEqual(result5, expected5)\n```\n#### Key steps: \n- with calling `when` function on the faked class and passing the `Fn` functions `keyPath` we can use the `thenReturns`\n- as a parameter of `thenReturns` function we can pass the return values\n- if we call the function more times then return values, a `MimicError.missingResult` error will be thrown\n\n#### `matchers`\nJust like in mocking frameworks, `mimic` has matchers as well, it could be use for teaching and verification.\n- `Arg.any` it could be anything without restrictions.\n- `Arg.eq(\u003cvalue\u003e)` it is an equality mathcer where the `\u003cvalue\u003e` means the expected value.\n- `Arg.nil` it expects that the value will be `nil`.\n- `Arg.notNil` it expects that the valu won't be `nil`.\n- `Arg.invokeClosure(\u003cclosure\u003e)` the given `closure` will be called when the function invocation happens.\n\n```swift\n    let expectedArgument = \"testExpectedArgument\"\n\n    fakeSomeClass.when(\\.fnSomeFunction).calledWith(Arg.eq(expectedArgument)).thenReturn(42)\n    \n    let result = testStruct.someFunction(with: expectedArgument)\n    \n    XCTAssertEqual(result, 42)\n```\n#### Key steps: \n- matchers can be used with `calledWith` method after `when` and `verify` as well\n- after `calledWith` other functions could be called, like `thenReturn`\n- in the example above, the `thenReturn` will be used only if the arguments passed to the funtion fits with the matchers\n\n#### `thenThrow`\nIt is just simply throw the given Error when the function being called.\n```swift\n    fakeSomeClass.when(\\.fnSomeFunction).thenThrow(error: TestError.magicWord)\n    \n    let result = testStruct.someFunction(with: \"value\")\n```\n\n### `verify` used for verification after the function usage\n\n#### `onThread(\u003cThread\u003e)` \nThis could be used to check on which thread the function was called.\n```swift\n    try fakeSomeClass.verify(\\.fnSomeFunction).on(thread: Thread.current)\n```\n#### `times(\u003ctimes value\u003e)` \nThis could be used to check how many times the function was called.\n```swift\n    try fakeSomeClass.verify(\\.fnSomeFunction).times(times: .atLeast(2))\n```\nPossible values:\n-  `.zero` it means the function should not be called\n- `.atLeast(\u003cInt value\u003e)` it means the function should be called `\u003cInt value\u003e` or more times\n- `.atMax(\u003cInt value\u003e)` it means the function should be called maximum `\u003cInt value\u003e` times\n- `.eq(\u003cInt value\u003e)` it means the function should be called exactly `\u003cInt value\u003e` times\n\n## Instance Generation for Codable Data Classes\n\nWhen working with data classes, it's often necessary to create instances of them for testing purposes. Mimic simplifies this process by automatically generating instances with random values. This feature becomes especially handy when dealing with complex data models or when you need to generate a large number of instances for testing different scenarios. With this feature you can save time and effort that you could spend instead for the actual testing.\n\n### `@Generate`\nTo use this feature you just simply add `@Generate` attribute before your variable.\n```swift\n    @Generate\n    var someStruct: SomeStruct\n```\nThat's it, after this code you could simply use the someStruct variable.\n\nLimitations:\n- the class itself should be implement the `Decodable` protocol\n- if the class contains enums or other complex properties, those have to implement the `Codable` protocol and an instance should be passed, to the `Generate` attribute.\n```swift\n    @Generate(SomeEnum.case1(42))\n    var someStruct: SomeStruct\n```\n\n# The cause\nThis team is the maker of the [EmarsysSDK's](https://github.com/emartech/ios-emarsys-sdk) when we started to develop the swift based version of it, we shortly arrived that point when we needed a proper mocking solution. We tried out few, but non of them was fit perfectly to our needs mainly because of those points what are highlighted above. This package and it's features are actively used in our SDK. Our team strongly beliefs that the good things, findings, solutions, knowledge should be shared that's why we made this repo publicly available to anyone. \nWe hope you will consider it useful and will help your work.\n\n## No promises\n\n\u003e `Disclaimer`\n\u003e\n\u003e As we mentioned before, at first place this package was created for internal use only. The main goal was to cover all of our use cases and fulfill our requirements what doesn't mean that this is good for everyone. If you find lack of features or issues we do not say that those will be implemented or solved in the near future since this product is not actively developed. We do our best, but no promises.\n\n## Logo\n\nThe logo was created by [Bing Image Creator](https://www.bing.com/create)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femartech%2Fmimic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femartech%2Fmimic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femartech%2Fmimic/lists"}