{"id":13537100,"url":"https://github.com/mockative/mockative","last_synced_at":"2026-02-28T07:02:08.917Z","repository":{"id":38240126,"uuid":"422101224","full_name":"mockative/mockative","owner":"mockative","description":"Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP)","archived":false,"fork":false,"pushed_at":"2024-11-28T13:22:26.000Z","size":654,"stargazers_count":301,"open_issues_count":26,"forks_count":14,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-23T08:04:51.065Z","etag":null,"topics":["android","ios","kotlin","kotlin-multiplaform","kotlin-native","kotlin-symbol-processor","ksp","mock","mocking","testing"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/mockative.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","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},"funding":{"github":["Nillerr"]}},"created_at":"2021-10-28T07:08:56.000Z","updated_at":"2025-05-16T15:55:47.000Z","dependencies_parsed_at":"2024-01-16T15:41:03.658Z","dependency_job_id":"43d07335-2a36-4e82-aca9-b5c710b16ef5","html_url":"https://github.com/mockative/mockative","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/mockative/mockative","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mockative%2Fmockative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mockative%2Fmockative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mockative%2Fmockative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mockative%2Fmockative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mockative","download_url":"https://codeload.github.com/mockative/mockative/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mockative%2Fmockative/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29927179,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T19:37:42.220Z","status":"online","status_checked_at":"2026-02-28T02:00:07.010Z","response_time":90,"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":["android","ios","kotlin","kotlin-multiplaform","kotlin-native","kotlin-symbol-processor","ksp","mock","mocking","testing"],"created_at":"2024-08-01T09:00:55.157Z","updated_at":"2026-02-28T07:02:08.904Z","avatar_url":"https://github.com/mockative.png","language":"Kotlin","funding_links":["https://github.com/sponsors/Nillerr"],"categories":["Libraries"],"sub_categories":["Test","🩺 Test"],"readme":"# Mockative\n\n[ksp]: https://github.com/google/ksp\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.mockative/mockative)](https://search.maven.org/artifact/io.mockative/mockative)\n\nMocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API ([KSP]).\nNotable features include:\n\n- Concise, non-intrusive, type-safe API\n- Mocking of classes and interfaces\n- Supports both [values](#stubbing-using-values) and [matchers](#stubbing-using-matchers) when\n  during stubbing [verification](#verification)\n- Supports [implicit stubbing](#implicit-stubbing-of-functions-returning-unit) of functions\n  returning `Unit`\n\n## Installation for Multiplatform projects\n\nAdd the `io.mockative` plugin and dependency to your **build.gradle.kts** file:\n\n```kotlin\nplugins {\n  id(\"io.mockative\") version \"3.2.3\"\n}\n\nkotlin {\n    commonMain {\n        dependencies {\n            implementation(\"io.mockative:mockative:3.2.3\")\n        }\n    }\n}\n```\n\nMockative is published to Maven Central and applies the following Gradle plugins:\n\n- `com.google.devtools.ksp`: Enables code generation of mockable implementations\n- `org.jetbrains.kotlin.plugin.allopen`: Enables mocking of classes\n\nMake sure you specify the following plugin repositories in your **build.settings.kts** file:\n\n```kotlin\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\n```\n\nThen add the following to your **gradle.properties** file:\n\n```properties\n#KSP\nksp.useKSP2=true\n```\n\n\u003e [!TIP]\n\u003e Now run your tests, which will copy a set of runtime dependencies to your generated code. These will disappear\n\u003e whenever you run a Gradle task that is not a test, lint of other verification task, but will reappear once you run\n\u003e one of them again.\n\nIf you're having the following error:\n\n\u003e KSP2: KtInvalidLifetimeOwnerAccessException: Access to invalid KtAlwaysAccessibleLifetimeToken: PSI has changed since\n\u003e creation\n\nPlease disable incremental processing for KSP by adding the following to your **gradle.properties** file:\n\n```properties\nksp.incremental=false\n```\n\nMockative 3 code generation should \"just work\", but if you're looking for more control over when Mockative code\ngeneration is enabled or disabled, please\nread [Controlling generation of mocks in Mockative 3](wiki/CONTROLLING_MOCKATIVE_3.md).\n\n### Making types mockable\n\nTo mock an interface or class with Mockative, annotate the type with the `@Mockable` annotation. This annotation\ntells Mockative to generate a mock implementation of the annotated type, which can be used in tests.\n\n```kotlin\nimport io.mockative.Mockable\n\n@Mockable\nclass MyService {\n    // ...\n}\n```\n\nThe Kotlin `all-open` plugin will automatically be applied to any `class` annotated with `@Mockable`.\n\n#### Mocking External Types\n\nSometimes we would like to mock types that are external to our module in our tests. To do so, pass a list of types\nyou'd like to mock in **any** `@Mockable` annotation in your main module. If you add the list of types to the\n`@Mockable` annotation of a type you'd also like to mock by itself, you must specify the type itself in the list of\ntypes to mock. By not specifying any types in the annotation, a mock for the annotated type itself is generated.\n\n```kotlin\nimport io.mockative.Mockable\n\n@Mockable(MyService::class, Clock::class)\nclass MyService {\n    // ...\n}\n```\n\n## Testing with Mockative\n\nObtaining an instance of a mock is as ease as calling the `\u003cT\u003e mock(KClass\u003cT\u003e)` function:\n\n```kotlin\nclass GitHubServiceTests {\n    val api = mock(of\u003cGitHubAPI\u003e())\n}\n```\n\nThen, to stub a function or property on the mock you have a couple of options:\n\n### Stubbing using values\n\nTo begin stubbing a function you may simply pass the values to the function call inside a block\npassed to the `every` or `coEvery` (when stubbing a `suspend` function) functions:\n\n```kotlin\n// Stub a blocking function\nevery { githubApi.getRepository(\"mockative/mockative\") }\n    .invokes { response }\n\n// Stub a `suspend` function (notice the use of `coEvery`)\ncoEvery { repositoryMapper.mapResponseToRepository(response) }\n    .invokes { repository }\n\n// Stub a property getter\nevery { repository.maintainer }\n    .returns(\"Nillerr\")\n\n// Stub a property setter (these are stubbed by default)\nevery { repository.stars = repository.stars + 1 }\n    .doesNothing()\n```\n\n### Stubbing using matchers\n\nSometimes when stubbing a function we're faced with difficulties providing a specified value for\none or more of the parameters of our expectation. In such cases we can use the matcher API to\nspecify the values our stub accepts:\n\n```kotlin\n// Assuming we want to stub the function in this S3Client:\ninterface S3Client {\n    suspend fun \u003cT\u003e getObject(input: GetObjectRequest, block: suspend (GetObjectResponse) -\u003e T): T\n}\n\n// Providing a value for the `block` parameter is difficult, so we can use the `any()` matcher to\n// specify that this expectation will match any value passed to that parameter. When we use one \n// matcher, we must use matchers for every parameter, and as such we must use the `eq(value)` \n// matcher to specify that we're expecting a specific request.\ncoEvery { s3Client.getObject\u003cFile\u003e(eq(request), any()) }\n    .returnsMany(expected)\n```\n\nAlso note how we're explicitly specifying the type parameter. We could do the same by explicitly\nspecifying it in the call to `any()` like this:\n\n```kotlin\nany\u003csuspend (GetObjectResponse) -\u003e File\u003e()\n```\n\nBut that would be significantly more verbose that the alternative.\n\n\u003e ❕Mockative has limited support for the matcher API when targeting Kotlin/Wasm (WASI).\n\n### Stubbing using functions\n\nYou may want to provide a function as an argument to another function, where you would like to be\nable to record invocations on that function. To do that, Mockative includes the interfaces `Fun[N]`\nand `CoFun[N]` (for `suspend` functions), each declaring a single function `invoke` that you can\npass as a mock to other functions in order to stub and verify invocations on it:\n\n```kotlin\n// Declare the mock function as a property in your test class\n@Mock\nval block = mock(of\u003cFun1\u003cGetObjectResponse, File\u003e\u003e())\n\n// Stub the mock function\nevery { block.invoke(response) }\n    .returns(file)\n\n// Call something that calls the mock function\ns3Client.getObject(request, block::invoke)\n\n// Verify the call to the mock function\nverify { block.invoke(response) }\n    .wasInvoked(exactly = once)\n```\n\n### Stubbing implementations\n\nThe following functions are available to provide a stub implementation for every expectation:\n\n| Function                                              | Description                                                                                                                                                                                                                      |\n|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `invokes(block: (args: Array\u003cAny?\u003e) -\u003e R)`            | Invokes the specified block. The arguments passed to the block are the arguments passed to the invocation.                                                                                                                       |\n| `invokes(block: () -\u003e R)`                             | Invokes the specified block.                                                                                                                                                                                                     |\n| `invokesMany(vararg block: (args: Array\u003cAny?\u003e) -\u003e R)` | Invokes the specified blocks in sequence. The arguments passed to the block are the arguments passed to the invocation. Once the last block in the sequence has been invoked, this stubbing will no longer match any invocation. |\n| `invokesMany(vararg blocks: () -\u003e R)`                 | Invokes the specified block. Once the last block in the sequence has been invoked, this stubbing will no longer match any invocation.                                                                                            |\n| `returns(value: R)`                                   | Returns the specified value.                                                                                                                                                                                                     |\n| `returnsMany(vararg value: R)`                        | Returns the specified values in sequence. Once the last value in the sequence has been returned, this stubbing will no longer match any invocation.                                                                              |\n| `throws(throwable: Throwable)`                        | Throws the specified exception.                                                                                                                                                                                                  |\n| `throwsMany(throwable: Throwable)`                    | Throws the specified exceptions in sequence. Once the last exception in the sequence has been thrown, this stubbing will no longer match any invocation.                                                                         |\n\nIn order to provide familiarity to developers coming from MockK, who prefer using infix notation,\nMockative also supports infix notation for the `invokes`, `returns`, and `throws` functions:\n\n```kotlin\nevery { api.getUsers() } returns users\n```\n\nWhen the return type of the function or property being stubbed is `Unit`, the following additional\nthen function is available:\n\n| Function        | Description     |\n|-----------------|-----------------|\n| `doesNothing()` | Returns `Unit`. |\n\n### Implicit stubbing of functions returning Unit\n\nMockative automatically stubs functions returning `Unit`, based on the idea that such functions are\ntypically used for verification rather than stubbing, and stubbing them could thus be considered\nboilerplate, while they are trivially automatically stubbed.\n\nYou can opt out of this behaviour on the project level through your **build.gradle.kts** file:\n\n**build.gradle.kts**\n\n```kotlin\nmockative {\n    stubsUnitByDefault = false\n}\n```\n\nAlternatively, you can opt out (or opt-in if you've opted out on the project level), using the\n`configure(mock, block)` function either inline:\n\n```kotlin\n@Mock\nval api = configure(mock(of\u003cGitHubAPI\u003e())) { stubsUnitByDefault = false }\n```\n\nOr as needed:\n\n```kotlin\n@Mock\nval api = mock(of\u003cGitHubAPI\u003e())\n\n@Test\nfun test() {\n    configure(api) { stubsUnitByDefault = false }\n}\n```\n\nThe configuration is stored on the mock instance. It must be configured before receiving any\ninvocations.\n\n## Generic Types\n\nWhen mocking a generic type use the `\u003cT\u003e of(): KClass\u003cT\u003e` function to retain the type\narguments when passed to the `\u003cT\u003e mock(KClass\u003cT\u003e)` function. You can use the `of` function\nregardless of whether you're mocking a generic or non-generic type.\n\n```kotlin\nclass GenericTypeTest {\n    @Mock\n    val list = mock(of\u003cList\u003cString\u003e\u003e())\n}\n```\n\n## Verification\n\nVerification of invocations on mocks is supported through the `verify` and `coVerify` functions\nusing either values or matchers as per the stubbing (`every`) API:\n\n### Verification using values\n\n```kotlin\n// Verify a `suspend` function (notice the use of `coVerify`)\ncoVerify { githubApi.getRepository(\"mockative/mockative\") }\n    .wasNotInvoked()\n\n// Verify a blocking function\nverify { repositoryMapper.mapResponseToRepository(response) }\n    .wasInvoked(atLeast = 1)\n\n// Verify a property getter\nverify { repository.maintainer }\n    .wasInvoked(atLeast = once, atMost = 6)\n\n// Verify a property setter\nverify { repository.stars = repository.stars + 1 }\n    .wasInvoked(exactly = 1)\n```\n\n### Verification using matchers\n\n```kotlin\n// Verify a suspend function (notice the use of `coVerify`)\ncoVerify { s3Client.getObject\u003cFile\u003e(eq(request), any()) }\n    .wasInvoked(exactly = once)\n```\n\n## Validation\n\nIn addition to verification of expectations there's also validation of the use of expectations,\nwhich, if not used, can lead to both \"under verification\" and \"over mocking\" resulting in\ninaccurate tests. To handle such cases Mockative provides 2 functions you can use on your mocks,\nusually in the `@AfterTest` function of your tests:\n\n```kotlin\n@AfterTest\nfun afterTest() {\n    // Verifies that all expectations were verified through a call to `verify { ... }.wasInvoked()`.\n    verifyNoUnverifiedExpectations(githubApi)\n\n    // Verifies that the mock has no expectations that weren't invoked at least once.\n    verifyNoUnmetExpectations(s3Client)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmockative%2Fmockative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmockative%2Fmockative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmockative%2Fmockative/lists"}