{"id":40724486,"url":"https://github.com/joke/spock-mockable","last_synced_at":"2026-01-21T14:03:49.051Z","repository":{"id":37930956,"uuid":"305405840","full_name":"joke/spock-mockable","owner":"joke","description":"Mock the un-mockable","archived":false,"fork":false,"pushed_at":"2026-01-12T21:57:18.000Z","size":405,"stargazers_count":29,"open_issues_count":9,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T01:57:39.937Z","etag":null,"topics":["mocking","spock-extension","spock-framework"],"latest_commit_sha":null,"homepage":"","language":"Java","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/joke.png","metadata":{"files":{"readme":"README.adoc","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.adoc","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":"2020-10-19T14:10:14.000Z","updated_at":"2025-04-02T03:39:36.000Z","dependencies_parsed_at":"2023-11-20T22:24:09.851Z","dependency_job_id":"a2725f05-d9aa-4faf-aefe-40bd53549c28","html_url":"https://github.com/joke/spock-mockable","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/joke/spock-mockable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joke%2Fspock-mockable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joke%2Fspock-mockable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joke%2Fspock-mockable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joke%2Fspock-mockable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joke","download_url":"https://codeload.github.com/joke/spock-mockable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joke%2Fspock-mockable/sbom","scorecard":{"id":529494,"data":{"date":"2025-08-11","repo":{"name":"github.com/joke/spock-mockable","commit":"6ae7d792e638a14e8122dc73e1ad06360a070153"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":5,"reason":"Found 5/9 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/autoapproval.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/autoapproval.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/codeql-analysis.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/codeql-analysis.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/joke/spock-mockable/release.yml/main?enable=pin","Info:   0 out of   7 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   8 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:17","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:18","Info: topLevel 'contents' permission set to 'read': .github/workflows/autoapproval.yml:8","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T05:22:09.668Z","repository_id":37930956,"created_at":"2025-08-20T05:22:09.668Z","updated_at":"2025-08-20T05:22:09.668Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28634787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["mocking","spock-extension","spock-framework"],"created_at":"2026-01-21T14:03:48.990Z","updated_at":"2026-01-21T14:03:49.040Z","avatar_url":"https://github.com/joke.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= `spock-mockable`\n:icons: font\n\nimage:https://github.com/joke/spock-mockable/workflows/build/badge.svg?branch=main[]\nimage:https://badgen.net/github/license/joke/spock-mockable[]\nimage:https://badgen.net/github/release/joke/spock-mockable/stable[]\nimage:https://badgen.net/github/dependabot/joke/spock-mockable[]\nimage:https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg[link=https://conventionalcommits.org]\nimage:https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit[pre-commit, link=https://github.com/pre-commit/pre-commit]\n\n`spock-mockable` allows creation of mocks otherwise un-mockable by the http://spockframework.org/[Spock Framework].\n\nSpock is not capable of mocking `private` or `final` classes or methods\nbecause they are not accessible via inheritance. `spock-mockable` uses JVM instrumentation to\nmodify these classes upon loading and apply looser access restrictions.\nIn consequence Spock is capable of mocking these classes. Refer to \u003c\u003cHow does it work\u003e\u003e.\n\n* Changes method visibility `private` to `protected`\n* Removes `final` from classes and methods\n* Supports mocking `static` methods\n* Automatically attaches java agent\n* Re-defines classes via https://bytebuddy.net/[Byte Buddy] transformation.\n* Working with Spock Framework 2.0, 2.1, 2.2 and 2.3\n\n== Setup\n\nAdd the artifact as an additional dependency to your spock setup.\n\nTIP: Even though not strictly necessary it is recommended to register the dependency as a java agent with the JVM. This way more classes can be transformed.\n\n=== Gradle\n\nimage:https://badgen.net/github/release/joke/spock-mockable/stable[]\n\n.build.gradle\n[source,groovy]\n----\ndependencies {\n    testImplementation 'io.github.joke:spock-mockable:x.y.z'\n}\n\n// to load the agent even earlier add it as a JVM argument\ntasks.withType(Test) {\n    jvmArgs += [\"-javaagent:${classpath.find { it.name.contains('spock-mockable') }.absolutePath}\"]\n}\n----\n\n=== Maven\n\n.pom.xml\n[source,xml]\n----\n\u003cdepenencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003eio.github.joke\u003c/groupId\u003e\n    \u003cartifactId\u003espock-mockable\u003c/artifactId\u003e\n    \u003cversion\u003ex.y.z\u003c/version\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n  \u003c/dependency\u003e\n\u003c/depenencies\u003e\n----\n\n== Usage\n\nUnder normal circumstances classes needing to undergo transformation are detected automatically.\n\n=== Mocks \u0026 Stubs\n\n.Mock definition\n[source,groovy]\n----\nclass MySpec extends Specification {\n    // either\n    Person person = Mock() // detected class based on type of variable\n    // or\n    def person = Mock(Person) // detected class based on Mock parameter\n}\n----\n\n.Stub definition\n[source,groovy]\n----\nclass MySpec extends Specification {\n    // either\n    Person person = Stub() // detected class based on type of variable\n    // or\n    def person = Stub(Person) // detected class based on Mock parameter\n}\n----\n\n=== Spies\n\n.Spy definition\n[source,groovy]\n----\nclass MySpec extends Specification {\n    // either\n    def person = new Person()\n    Person personSpy = Spy(person) // detected class based on type of variable\n\n    // or\n    Person person = new Person()\n    def personSpy = Spy(personInstance) // detected class based on Mock parameter\n\n\n    // WARNING!\n    def person = new Person()\n    def person2 = personInstance // person2 is dynamic typed and ...\n    def personSpy = Spy(person2) // ... class type information is lost in this case!\n}\n----\n\n=== Static methods\n\nStatic methods can be mocked in a similar fashion like https://spockframework.org/spock/docs/2.3/interaction_based_testing.html#_mocking_static_methods[mocking static methods using `GroovySpy`]. All classes which have undergone transformation are\nalready prepared for mocking there static methods. The original method is called by default similar to a spy.\n\nIf an interaction has been registered within a feature method the real method is skipped and the interaction is executed.\n\nA call of the method can also be forced by using\nhttps://spockframework.org/spock/docs/2.3/interaction_based_testing.html#Spies[`{ callRealMethod*() }`].\n\nNOTE: Specifications defining interactions with static mocks are automatically annotated with\nhttps://spockframework.org/spock/docs/2.3/parallel_execution.html#_isolated_execution[`@Isolated`].\n\n.Mock static methods\n[source,groovy]\n----\nclass UtilitySpec extends Specification {\n    def 'mock static method'() {\n        setup:\n        Spy(UtilityClass) // needed if class has not been mocked/spied/stubbed prior\n\n        when:\n        def res = UtilityClass.someStaticMethod('hello')\n\n        then:\n        1 * UtilityClass.someStaticMethod('hello') \u003e\u003e 'mock value'\n\n        expect:\n        res == 'mock value'\n    }\n}\n----\n\nMore examples can be found in link:examples[].\n\n=== Transform special cases\n\nIn special cases you might want to manually specify additional classes or packages to undergo transformation. This need might arise if the exact class type can not be referenced in the specification. In this case you can specify arbitrary class or package names manually.\n\n.Mockable annotation\n[source,groovy]\n----\n@Mockable(className = 'some.package.MyFirstClass')\n@Mockable(className = 'some.package.MySecondClass')\n@Mockable(packageName = 'some.package')\nclass PersonSpec extends Specification {\n}\n----\n\n== How does it work\n\nDuring groovy's compilation phase each specification is analyzed and mock invocations are detected.\nAt the start of a test JVM these detected classes are transformed by the JVM instrumentation\nregardless of the actual specification there the mock invocation has been detected.\nThis might lead to unexpected behaviour between different specifications.\n\nFor the earliest possible transformation of classes start the agent by using the JVM argument (`-javaagent`).\n\nIMPORTANT: For agent instrumentation to work the JVM must support this feature. A JRE is most likely not sufficient.\n\nIMPORTANT: The agent is attached to the JVM as early as possible but some classes not be transformed nevertheless because they are used prior. This restriction applies to some `java.lang` classes but also to some junit classes.\n\n=== Conditionally disable transformation\n\nYou can disable `spock-mockable` by setting the JVM system property `spock-mockable.disabled=true`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoke%2Fspock-mockable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoke%2Fspock-mockable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoke%2Fspock-mockable/lists"}