{"id":13413929,"url":"https://github.com/xhd2015/xgo","last_synced_at":"2026-03-06T01:03:10.148Z","repository":{"id":227490282,"uuid":"771575307","full_name":"xhd2015/xgo","owner":"xhd2015","description":"All-in-one go testing library","archived":false,"fork":false,"pushed_at":"2025-11-26T16:36:49.000Z","size":4490,"stargazers_count":428,"open_issues_count":66,"forks_count":26,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-11-29T10:44:53.671Z","etag":null,"topics":["coverage","coverage-report","go","go-test","go-testing","golang","gomonkey","incremental-coverage","instrumentation","mock","monkey-patching","test-explorer","testing","testing-library","tracing","unit-test","unit-testing","xgo"],"latest_commit_sha":null,"homepage":"","language":"Go","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/xhd2015.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"support/assert/diff.go","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":"2024-03-13T14:51:09.000Z","updated_at":"2025-11-27T23:40:21.000Z","dependencies_parsed_at":"2024-03-13T16:05:54.285Z","dependency_job_id":"102eda45-deae-418b-a214-aede8b5792e6","html_url":"https://github.com/xhd2015/xgo","commit_stats":{"total_commits":301,"total_committers":7,"mean_commits":43.0,"dds":0.02657807308970095,"last_synced_commit":"1d8b33c2ca5fafec9439a1ec7adcdc591db705b9"},"previous_names":["xhd2015/xgo"],"tags_count":132,"template":false,"template_full_name":null,"purl":"pkg:github/xhd2015/xgo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xhd2015%2Fxgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xhd2015%2Fxgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xhd2015%2Fxgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xhd2015%2Fxgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xhd2015","download_url":"https://codeload.github.com/xhd2015/xgo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xhd2015%2Fxgo/sbom","scorecard":{"id":1239475,"data":{"date":"2025-10-27","repo":{"name":"github.com/xhd2015/xgo","commit":"4343cde38be77f5a8ca82d0222e2e388dbdae382"},"scorecard":{"version":"v5.3.1-0.20251027165727-e1a061403784","commit":"e1a06140378460c4ed2472c24909d40b3d9fb9ac"},"score":1.7,"checks":[{"name":"Maintained","score":3,"reason":"0 commit(s) and 4 issue activity found in the last 90 days -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/10 approved changesets -- score normalized to 0","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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#code-review"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#license"}},{"name":"Dangerous-Workflow","score":0,"reason":"dangerous workflow patterns detected","details":["Warn: script injection with untrusted input ' github.head_ref ': .github/workflows/check-merge.yml:23","Warn: script injection with untrusted input ' github.head_ref ': .github/workflows/check-merge.yml:31"],"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#dangerous-workflow"}},{"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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#cii-best-practices"}},{"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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#packaging"}},{"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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md","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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#branch-protection"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/check-merge.yml:1","Warn: no topLevel permission defined: .github/workflows/check.yml:1","Warn: no topLevel permission defined: .github/workflows/custom.yml:1","Warn: no topLevel permission defined: .github/workflows/go-compatible.yml:1","Warn: no topLevel permission defined: .github/workflows/go-debug.yml:1","Warn: no topLevel permission defined: .github/workflows/go-next.yml:1","Warn: no topLevel permission defined: .github/workflows/go-windows.yml:1","Warn: no topLevel permission defined: .github/workflows/go.yml:1","Warn: no topLevel permission defined: .github/workflows/large-projects.yml:1","Warn: no topLevel permission defined: .github/workflows/nightly-test.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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#token-permissions"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.1.10 not signed: https://api.github.com/repos/xhd2015/xgo/releases/229013301","Warn: release artifact v1.1.9 not signed: https://api.github.com/repos/xhd2015/xgo/releases/226900957","Warn: release artifact v1.1.8 not signed: https://api.github.com/repos/xhd2015/xgo/releases/226349144","Warn: release artifact v1.1.7 not signed: https://api.github.com/repos/xhd2015/xgo/releases/215334280","Warn: release artifact v1.1.6 not signed: https://api.github.com/repos/xhd2015/xgo/releases/215076037","Warn: release artifact v1.1.10 does not have provenance: https://api.github.com/repos/xhd2015/xgo/releases/229013301","Warn: release artifact v1.1.9 does not have provenance: https://api.github.com/repos/xhd2015/xgo/releases/226900957","Warn: release artifact v1.1.8 does not have provenance: https://api.github.com/repos/xhd2015/xgo/releases/226349144","Warn: release artifact v1.1.7 does not have provenance: https://api.github.com/repos/xhd2015/xgo/releases/215334280","Warn: release artifact v1.1.6 does not have provenance: https://api.github.com/repos/xhd2015/xgo/releases/215076037"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#signed-releases"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/check-merge.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check-merge.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/check-merge.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check-merge.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/check.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/check.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/check.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/check.yml:49: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/check.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/custom.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/custom.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/custom.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/custom.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-compatible.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-compatible.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-compatible.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-compatible.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-debug.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-debug.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-debug.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-debug.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-next.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-next.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-windows.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-windows.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-windows.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go-windows.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:49: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:60: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:82: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/large-projects.yml:112: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/large-projects.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:80: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:92: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nightly-test.yml:136: update your workflow using https://app.stepsecurity.io/secureworkflow/xhd2015/xgo/nightly-test.yml/master?enable=pin","Warn: npmCommand not pinned by hash: cmd/xgo/internal/vendir/github.com/xhd2015/go-coverage/diff/vscode/gen.sh:3","Warn: goCommand not pinned by hash: .github/workflows/custom.yml:56","Warn: pipCommand not pinned by hash: .github/workflows/nightly-test.yml:113","Info:   1 out of  29 GitHub-owned GitHubAction dependencies pinned","Info:   2 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned","Info:   4 out of   5 goCommand dependencies pinned","Info:   0 out of   1 pipCommand 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/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"26 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-4vvj-4cpr-p986","Warn: Project is vulnerable to: GHSA-hc6q-2mpp-qw7j","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2021-0356 / GHSA-8c26-wmh5-6g9v","Warn: Project is vulnerable to: GO-2022-0968 / GHSA-gwc9-m7rh-j2ww","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2022-1059 / GHSA-69ch-w2m2-3vjp","Warn: Project is vulnerable to: GO-2021-0113 / GHSA-ppp9-7jff-5vj2","Warn: Project is vulnerable to: GO-2023-1988 / GHSA-2wrh-6pvc-2jm9","Warn: Project is vulnerable to: GO-2023-2102 / GHSA-4374-p667-p6c8","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2023-2153 / GHSA-m425-mq94-257g / GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2024-2611 / GHSA-8r3f-844c-mc37"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/e1a06140378460c4ed2472c24909d40b3d9fb9ac/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-11-05T04:14:08.352Z","repository_id":227490282,"created_at":"2025-11-05T04:14:08.352Z","updated_at":"2025-11-05T04:14:08.352Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30156864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["coverage","coverage-report","go","go-test","go-testing","golang","gomonkey","incremental-coverage","instrumentation","mock","monkey-patching","test-explorer","testing","testing-library","tracing","unit-test","unit-testing","xgo"],"created_at":"2024-07-30T20:01:52.928Z","updated_at":"2026-03-06T01:03:10.095Z","avatar_url":"https://github.com/xhd2015.png","language":"Go","funding_links":[],"categories":["测试","Go","Template Engines","Testing"],"sub_categories":["Mock"],"readme":"\n# xgo\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/xhd2015/xgo.svg)](https://pkg.go.dev/github.com/xhd2015/xgo)\n[![Go Report Card](https://goreportcard.com/badge/github.com/xhd2015/xgo)](https://goreportcard.com/report/github.com/xhd2015/xgo)\n[![Slack Widget](https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true\u0026logo=slack\u0026colorB=red)](https://join.slack.com/t/golang-xgo/shared_invite/zt-2ixe161jb-3XYTDn37U1ZHZJSgnQi6sg)\n[![Go Coverage](https://img.shields.io/badge/Coverage-82.6%25-brightgreen)](https://github.com/xhd2015/xgo/actions)\n[![CI](https://github.com/xhd2015/xgo/workflows/Go/badge.svg)](https://github.com/xhd2015/xgo/actions)\n[![Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)\n\n**English | [简体中文](./README_zh_cn.md)**\n\n`xgo` provides *all-in-one* test utilities for golang, including:\n\n- [API](#api):\n  - [Mock](#patch)\n  - [Trace](#trace)\n  - [Trap](#trap)\n- [Tools](#tools):\n  - [Test Explorer](#test-explorer)\n  - [Incremental Coverage](#incremental-coverage)\n- [IDE Setup](#ide-setup)\n\nAs for the monkey patching part, `xgo` works as a preprocessor for `go run`,`go build`, and `go test`(see our [blog](https://blog.xhd2015.xyz/posts/xgo-monkey-patching-in-go-using-toolexec)).\n\nSee [Quick Start](#quick-start) and [Documentation](./doc) for more details.\n\n[![Xgo Test Explorer](https://github.com/xhd2015/xgo/assets/14964938/53de1de1-85a6-41b0-9300-c4979ba0096c)](https://youtu.be/L8oPhgqtdGg)\n\n![Xgo Test Explorer](https://github.com/xhd2015/xgo/assets/14964938/2666f0c9-3a9f-45e0-a1e2-20e1bf401fa1)\n\n\u003e *By the way, I promise you this is an interesting project.*\n\n# Installation\n```sh\ngo install github.com/xhd2015/xgo/cmd/xgo@latest\n```\n\nVerify the installation:\n```sh\nxgo version\n# output:\n#   1.0.x\n\nxgo help\n# output: help messages\n```\n\nIf `xgo` is not found, you may need to check if `$GOPATH/bin` is added to your `PATH` variable.\n\nFor CI jobs like github workflow, see [doc/INSTALLATION.md](./doc/INSTALLATION.md).\n\n# Requirement\n`xgo` requires at least `go1.17` to compile.\n\nThere is no specific limitation on OS and Architecture. \n\n**All OS and Architectures** are supported by `xgo` as long as they are supported by `go`.\n|         | x86 | x86_64 (amd64)  | arm64     | any other Arch...     |\n|:---------|:-----------:|:-----------:|:-----------:|:-----------:|\n| Linux   | Y | Y | Y | Y |\n| Windows | Y | Y | Y | Y |\n| macOS   | Y | Y | Y | Y |\n| any other OS... | Y | Y | Y | Y|\n\n# Quick Start\nLet's write a unit test with `xgo`:\n\n1. Ensure you have installed `xgo` by following the [Installation](#installation) section, and verify the installation with:\n```sh\nxgo version\n# output\n#   1.0.x\n```\n\n2. Init a go project:\n```sh\nmkdir demo\ncd demo\ngo mod init demo\n```\n3. Add `demo_test.go`:\n```go\npackage demo_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/xhd2015/xgo/runtime/mock\"\n)\n\nfunc MyFunc() string {\n\treturn \"my func\"\n}\n\nfunc TestFuncMock(t *testing.T) {\n\tmock.Patch(MyFunc, func() string {\n\t\treturn \"mock func\"\n\t})\n\ttext := MyFunc()\n\tif text != \"mock func\" {\n\t\tt.Fatalf(\"expect MyFunc() to be 'mock func', actual: %s\", text)\n\t}\n}\n```\n4. Get the `github.com/xhd2015/xgo/runtime` dependency:\n```sh\ngo get github.com/xhd2015/xgo/runtime\n```\n5. Test the code:\n```sh\nxgo test -v ./\n# NOTE: xgo will take some time to setup for the first time\n```\n\nOutput:\n```sh\n=== RUN   TestFuncMock\n--- PASS: TestFuncMock (0.00s)\nPASS\nok      demo\n```\n\nNOTE: `xgo` is used to test your code, not just `go`. \n\nUnder the hood, `xgo` preprocess your code to add mock hooks, and then calls `go` to do remaining jobs.\n\nThe above code can be found at [doc/demo](./doc/demo).\n\n# API\n\n## Patch\nPatch given function within current goroutine.\n\nThe API:\n- `Patch(fn,replacer) func()`\n\nCheatsheet:\n```go\n// package level func\nmock.Patch(SomeFunc, mockFunc)\n\n// per-instance method\n// only the bound instance `v` will be mocked\n// `v` can be either a struct or an interface\nmock.Patch(v.Method, mockMethod)\n\n// per-TParam generic function\n// only the specified `int` version will be mocked\nmock.Patch(GenericFunc[int], mockFuncInt)\n\n// per TParam and instance generic method\nv := GenericStruct[int]\nmock.Mock(v.Method, mockMethod)\n\n// closure can also be mocked\n// less used, but also supported\nmock.Mock(closure, mockFunc)\n```\n\nParameters:\n- If `fn` is a simple function(i.e. a package level function, or a function owned by a type, or a closure(yes, we do support mocking closures)),then all call to that function will be intercepted,\n- `replacer` another function that will replace `fn`\n\nScope:\n- If `Patch` is called from `init`, then all goroutines will be mocked.\n- Otherwise, `Patch` is called after `init`, then the mock interceptor will only be effective for current goroutine, other goroutines are not affected.\n\nNOTE: `fn` and `replacer` should have the same signature.\n\nReturn:\n- a `func()` can be used to remove the replacer earlier before current goroutine exits\n\nPatch replaces the given `fn` with `replacer` in current goroutine. It will remove the replacer once current goroutine exits.\n\nExample:\n```go\npackage patch_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/xhd2015/xgo/runtime/mock\"\n)\n\nfunc greet(s string) string {\n\treturn \"hello \" + s\n}\n\nfunc TestPatchFunc(t *testing.T) {\n\tmock.Patch(greet, func(s string) string {\n\t\treturn \"mock \" + s\n\t})\n\n\tres := greet(\"world\")\n\tif res != \"mock world\" {\n\t\tt.Fatalf(\"expect patched result to be %q, actual: %q\", \"mock world\", res)\n\t}\n}\n```\n\n`Patch`(and `Mock` below) also works for package-level variables:\n\n```go\npackage patch\n\nimport (\n    \"testing\"\n\n    \"github.com/xhd2015/xgo/runtime/mock\"\n)\n\nvar a int = 123\n\nfunc TestPatchVarTest(t *testing.T) {\n\tmock.Patch(\u0026a, func() int {\n\t\treturn 456\n\t})\n\tb := a\n\tif b != 456 {\n\t\tt.Fatalf(\"expect patched variable a to be %d, actual: %d\", 456, b)\n\t}\n}\n```\nSee [runtime/mock/MOCK_VAR_CONST.md](runtime/mock/MOCK_VAR_CONST.md).\n\n## Mock\n`runtime/mock` also provides another API called `Mock`, which is similar to `Patch`.\n\nThe the only difference between them lies in the the second parameter: `Mock` accepts an interceptor. \n\n`Mock` can be used where `Patch` cannot be used, such as functions with unexported type.\n\n\u003e API details: [runtime/mock/README.md](runtime/mock)\n\nThe Mock API:\n- `Mock(fn, interceptor)`\n\nParameters:\n- `fn` same as described in [Patch](#patch) section\n- If `fn` is a method(i.e. `file.Read`),then only call to the instance will be intercepted, other instances will not be affected\n\nInterceptor Signature: `func(ctx context.Context, fn *core.FuncInfo, args core.Object, results core.Object) error`\n- If the interceptor returns `nil`, then the target function is mocked,\n- If the interceptor returns `mock.ErrCallOld`, then the target function is called again,\n- Otherwise, the interceptor returns a non-nil error, that will be set to the function's return error.\n\nThere are other 2 APIs can be used to setup mock based on name, check [runtime/mock/README.md](runtime/mock/README.md) for more details.\n\nMethod mock example:\n```go\ntype MyStruct struct {\n    name string\n}\nfunc (c *MyStruct) Name() string {\n    return c.name\n}\n\nfunc TestMethodMock(t *testing.T){\n    myStruct := \u0026MyStruct{\n        name: \"my struct\",\n    }\n    otherStruct := \u0026MyStruct{\n        name: \"other struct\",\n    }\n    mock.Mock(myStruct.Name, func(ctx context.Context, fn *core.FuncInfo, args core.Object, results core.Object) error {\n        results.GetFieldIndex(0).Set(\"mock struct\")\n        return nil\n    })\n\n    // myStruct is affected\n    name := myStruct.Name()\n    if name!=\"mock struct\"{\n        t.Fatalf(\"expect myStruct.Name() to be 'mock struct', actual: %s\", name)\n    }\n\n    // otherStruct is not affected\n    otherName := otherStruct.Name()\n    if otherName!=\"other struct\"{\n        t.Fatalf(\"expect otherStruct.Name() to be 'other struct', actual: %s\", otherName)\n    }\n}\n```\n\n## Trace\n\u003e Trace might be the most powerful tool provided by xgo, this blog has a more thorough example: https://blog.xhd2015.xyz/posts/xgo-trace_a-powerful-visualization-tool-in-go\n\nIt is painful when debugging with a deep call stack.\n\nTrace addresses this issue by collecting the hierarchical stack trace and stores it into file for later use.\n\nNeedless to say, with Trace, debug becomes less usual:\n\n```go\npackage trace_test\n\nimport (\n    \"fmt\"\n    \"testing\"\n)\n\nfunc TestTrace(t *testing.T) {\n    A()\n    B()\n    C()\n}\n\nfunc A() { fmt.Printf(\"A\\n\") }\nfunc B() { fmt.Printf(\"B\\n\");C(); }\nfunc C() { fmt.Printf(\"C\\n\") }\n```\n\nRun with `xgo`:\n\n```sh\n# run the test\n# this will write the trace into TestTrace.json\n# --strace represents stack trace\nxgo test --strace ./\n\n# view the trace\nxgo tool trace TestTrace.json\n```\n\nOutput:\n![trace html](doc/img/trace_demo.jpg \"Simple Trace\")\n\nAnother more complicated example from [runtime/test/stack_trace/update_test.go](runtime/test/stack_trace/update_test.go): \n![trace html](cmd/xgo/trace/testdata/stack_trace.jpg \"Complicatd Trace\")\n\nReal world examples: \n- https://github.com/Shibbaz/GOEventBus/pull/11\n\nTrace helps you get started to a new project quickly.\n\nBy default, Trace will write traces to a temp directory under current working directory. This behavior can be overridden by setting `--strace-dir=\u003cDIR\u003e`.\n\nBesides the `--strace` flag, xgo allows you to define which span should be collected, using `trace.Trace()`:\n```go\nimport \"github.com/xhd2015/xgo/runtime/trace\"\n\nfunc TestTrace(t *testing.T) {\n    A()\n    trace.Trace(trace.Config{OutputFile:\"demo.json\"},nil,func() (interface{},error){\n        B()\n        C()\n    return nil,nil\n    })\n}\n```\nThe trace will only include `B()` and `C()`.\n\n## Recorder\nThe following example logs function execution trace by adding a Recorder:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/xhd2015/xgo/runtime/trace\"\n)\n\nfunc main() {\n    trace.RecordCall(A, func(){\n        fmt.Printf(\"record A\\n\")\n    })\n    trace.RecordCall(B,func(){\n        fmt.Printf(\"record B\\n\")\n    })\n    A()\n    B()\n}\n\nfunc A() {\n    fmt.Printf(\"A\\n\")\n}\n\nfunc B() {\n    fmt.Printf(\"B\\n\")\n}\n```\n\nRun with `xgo`:\n\n```sh\nxgo run ./\n# output:\n#   record A\n#   A\n#   record B\n#   B\n```\n\n`RecordCall()` add given recorder to either global or local, depending on whether it is called from `init` or after `init`:\n- Before `init`: effective globally for all goroutines,\n- After `init`: effective only for current goroutine, and will be cleared after current goroutine exits.\n\nWhen `RecordCall()` is called after `init`, it will return a dispose function to clear the recorder earlier before current goroutine exits.\n\n## Trap\n`xgo` **preprocess** the source code before invoking `go`, providing a chance for user to intercept any function when called.\n\nTrap allows developer to intercept function execution on the fly.\n\nTrap is the core of `xgo` as it is the basis of other abilities like Mock and Trace.\n\nThe following example logs function execution trace by adding a Trap interceptor:\n\n(check [test/testdata/trap/trap.go](test/testdata/trap/trap.go) for more details.)\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/xhd2015/xgo/runtime/core\"\n    \"github.com/xhd2015/xgo/runtime/trap\"\n)\n\nfunc init() {\n    trap.AddInterceptor(\u0026trap.Interceptor{\n        Pre: func(ctx context.Context, f *core.FuncInfo, args core.Object, results core.Object) (interface{}, error) {\n            if f.Name == \"A\" {\n                fmt.Printf(\"trap A\\n\")\n                return nil, nil\n            }\n            if f.Name == \"B\" {\n                fmt.Printf(\"abort B\\n\")\n                return nil, trap.ErrAbort\n            }\n            return nil, nil\n        },\n    })\n}\n\nfunc main() {\n    A()\n    B()\n}\n\nfunc A() {\n    fmt.Printf(\"A\\n\")\n}\n\nfunc B() {\n    fmt.Printf(\"B\\n\")\n}\n```\n\nRun with `go`:\n\n```sh\ngo run ./\n# output:\n#   A\n#   B\n```\n\nRun with `xgo`:\n\n```sh\nxgo run ./\n# output:\n#   trap A\n#   A\n#   abort B\n```\n\n`AddInterceptor()` add given interceptor to either global or local, depending on whether it is called from `init` or after `init`:\n- Before `init`: effective globally for all goroutines,\n- After `init`: effective only for current goroutine, and will be cleared after current goroutine exits.\n\nWhen `AddInterceptor()` is called after `init`, it will return a dispose function to clear the interceptor earlier before current goroutine exits.\n\nExample:\n\n```go\nfunc main(){\n    clear := trap.AddInterceptor(...)\n    defer clear()\n    ...\n}\n```\n\n# Tools\n## Test Explorer\n\nThe `xgo e` sub command will open a test explorer UI in the browser, provide an easy way to test and debug go code.\n\nWith the test explorer, `xgo test` is used instead of `go test`, to enable mocking functionalities.\n\n```sh\n$ xgo e\nServer listen at http://localhost:7070\n```\n\n\u003cimg width=\"1792\" alt=\"test-explorer\" src=\"https://github.com/xhd2015/xgo/assets/14964938/f8689a2d-b422-4101-acef-0aa41ad7e246\"\u003e\n\nP.S.: `xgo e` is an alias for `xgo tool test-explorer`.\n\nFor help, run `xgo e help`. \n\nSee [doc/test-explorer/README.md](doc/test-explorer) for configuration details.\n\n# Incremental Coverage\nThe `xgo tool coverage` sub command extends go's builtin `go tool cover` for better visualization.\n\nFirst, run `go test` or `xgo test` to get a coverage profile:\n```sh\ngo test -cover -coverpkg ./... -coverprofile cover.out ./...\n``` \n\nThen, use `xgo` to display the coverage:\n```sh\nxgo tool coverage serve cover.out\n```\n\nOutput:\n\n![coverage](doc/img/coverage.jpg \"Coverage\")\n\nThe displayed coverage is a combination of coverage and git diff. By default, only modified lines were shown:\n- Covered lines shown as light blue,\n- Uncovered lines shown as light yellow\n\nThis helps to quickly locate changes that were not covered, and add tests for them incrementally.\n\n# IDE Setup\nTo use `xgo` with IDEs like VSCode, GoLand and many others, follow these steps:\n- setup GOROOT\n```sh\nxgo setup\n```\n\nThis will output an xgo-instrumented GOROOT from your current GOROOT:\n```sh\n/Users/xhd2015/.xgo/go-instrument/go1.24.2_Us_xh_in_go_994c1863/go1.24.2\n```\n\n- Add instrumented GOROOT to IDE's env\n  - VSCode: add to `.vscode/settings.json`\n```json\n{\n    \"go.goroot\": \"/Users/xhd2015/.xgo/go-instrument/go1.24.2_Us_xh_in_go_994c1863/go1.24.2\",\n    \"go.testFlags\": [\n        \"-v\"\n    ]\n}\n```\n  - GoLand: add to project settings, see [GoLand: GOROOT and GOPATH](https://www.jetbrains.com/help/go/configuring-goroot-and-gopath.html)\n  - Others: refer to the IDE's documentation\n\nTo debug `xgo`, you can add `XGO_FLAGS=--log-debug ...` to IDE's env, which will automatically applied when `xgo` runs.\n\nYou can also use `xgo exec \u003ccmd\u003e` to run commands under the `xgo`'s environment:\n```sh\nxgo exec go test -v ./\n```\n\n# Concurrent safety\nI know you guys from other monkey patching library suffer from the unsafety implied by these frameworks.\n\nBut I guarantee you mocking in xgo is builtin concurrent safe. That means, you can run multiple tests concurrently as long as you like.\n\nWhy? when you run a test, you setup some mock, these mocks will only affect the goroutine test running the test. And these mocks get cleared when the goroutine ends, no matter the test passed or failed.\n\nWant to know why? Stay tuned, we are working on internal documentation.\n\n# Implementation Details\nThis blog has a basic explanation: https://blog.xhd2015.xyz/posts/xgo-monkey-patching-in-go-using-toolexec\n\nAnd you can also check the generated `.xgo/gen` directory, which contains all instrumented overlay files.\n\nSee [Issue#7](https://github.com/xhd2015/xgo/issues/7) for more details.\n\n# Why `xgo`?\nThe reason is simple: **NO** interface.\n\nYes, no interface, just for mocking. If the only reason to abstract an interface is to mock, then it only makes me feel boring, not working.\n\nExtracting interface just for mocking is never an option to me. To the domain of the problem, it's merely a workaround. It enforces the code to be written in one style, that's why we don't like it.\n\nMonkey patching simply does the right thing for the problem. But existing library are bad at compatibility.\n\nSo I created `xgo`, and hope it will finally take over other solutions to the mocking problem.\n\n# Comparing `xgo` with `monkey`\nThe project [bouk/monkey](https://github.com/bouk/monkey), was initially created by bouk, as described in his blog https://bou.ke/blog/monkey-patching-in-go.\n\nIn short, it uses a low level assembly hack to replace function at runtime. Which exposes lots of confusing problems to its users as it gets used more and more widely(especially on macOS).\n\nThen it was archived and no longer maintained by the author himself. However, two projects later take over the ASM idea and add support for newer go versions and architectures like Apple M1.\n\nStill, the two does not solve the underlying compatibility issues introduced by ASM, including cross-platform support, the need to write to a read-only section of the execution code and lacking of general mock.\n\nSo developers still get annoying failures every now and then.\n\nXgo managed to solve these problems by avoiding low level hacking of the language itself. Instead, it relies on the IR representation employed by the go compiler.\n\nIt does a so-called `IR Rewriting` on the fly when the compiler compiles the source code. The IR(Intermediate Representation) is closer to the source code rather than the machine code. Thus it is much more stable than the monkey solution.\n\nIn conclusion, `xgo` and monkey are compared as the following:\n||xgo|monkey|\n|-|-|-|\n|Technique|IR|ASM|\n|Function Mock|Y|Y|\n|Unexported Function Mock|Y|N|\n|Per-Instance Method Mock|Y|N|\n|Per-Goroutine Mock|Y|N|\n|Per-Generic Type Mock|Y|Y|\n|Var Mock|Y|N|\n|Const Mock|Y|N|\n|Closure Mock|Y|Y|\n|Stack Trace|Y|N|\n|General Trap|Y|N|\n|Compatibility|NO LIMIT|limited to amd64 and arm64|\n|API|simple|complex|\n|Integration Effort|easy|hard|\n\n# Contribution\nWant to help contribute to `xgo`? Great! Check [CONTRIBUTING\n](CONTRIBUTING.md) for help.\n\n# Evolution of `xgo`\n`xgo` is the successor of the original [go-mock](https://github.com/xhd2015/go-mock), which works by rewriting go code before compile.\n\nThe strategy employed by `go-mock` works well but causes much longer build time for larger projects due to source code explosion.\n\nHowever, `go-mock` is remarkable for it's discovery of Trap, Trace besides Mock, and additional abilities like trapping variable and disabling map randomness.\n\nIt is the shoulder which `xgo` stands on.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxhd2015%2Fxgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxhd2015%2Fxgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxhd2015%2Fxgo/lists"}