{"id":43511346,"url":"https://github.com/testdouble/quibble","last_synced_at":"2026-02-03T13:10:31.559Z","repository":{"id":41251420,"uuid":"44144942","full_name":"testdouble/quibble","owner":"testdouble","description":"Makes it easy to replace require'd dependencies.","archived":false,"fork":false,"pushed_at":"2024-06-01T01:18:13.000Z","size":507,"stargazers_count":96,"open_issues_count":19,"forks_count":26,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-08-09T13:53:38.033Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/testdouble.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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":"2015-10-13T01:46:55.000Z","updated_at":"2025-06-22T14:19:50.000Z","dependencies_parsed_at":"2024-06-01T02:32:40.337Z","dependency_job_id":"df91fc26-0ac5-452d-8218-e95a0b22e273","html_url":"https://github.com/testdouble/quibble","commit_stats":{"total_commits":179,"total_committers":17,"mean_commits":"10.529411764705882","dds":"0.26256983240223464","last_synced_commit":"7bc7cbd0951e8f47b659b4f97a83292e9c330591"},"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/testdouble/quibble","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fquibble","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fquibble/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fquibble/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fquibble/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testdouble","download_url":"https://codeload.github.com/testdouble/quibble/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fquibble/sbom","scorecard":{"id":875075,"data":{"date":"2025-08-11","repo":{"name":"github.com/testdouble/quibble","commit":"fac450e65d68a00411a7ed51fc29f19af581f95b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"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":"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/node.js.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":"Code-Review","score":2,"reason":"Found 4/16 approved changesets -- score normalized to 2","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":"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":"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":"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":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/testdouble/quibble/node.js.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/testdouble/quibble/node.js.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 npmCommand 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":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":"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":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"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":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 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"}},{"name":"Vulnerabilities","score":4,"reason":"6 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T05:40:06.634Z","repository_id":41251420,"created_at":"2025-08-24T05:40:06.634Z","updated_at":"2025-08-24T05:40:06.634Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29046503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T10:09:22.136Z","status":"ssl_error","status_checked_at":"2026-02-03T10:09:16.814Z","response_time":96,"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":[],"created_at":"2026-02-03T13:10:30.695Z","updated_at":"2026-02-03T13:10:31.539Z","avatar_url":"https://github.com/testdouble.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# quibble\n\n[![Build Status](https://travis-ci.org/testdouble/quibble.svg?branch=main)](https://travis-ci.org/testdouble/quibble)\n\nQuibble is a terser (and more magical) alternative to packages like\n[proxyquire](https://github.com/thlorenz/proxyquire),\n[sandboxed-module](https://github.com/felixge/node-sandboxed-module) and\n[mockery](https://github.com/mfncooper/mockery) for mocking out dependencies\nin tests of Node.js modules. Using `quibble` you can replace\nhow `require()` will behave for a given path. Its intended use is squarely\nfocused on unit testing. It is almost-but-not-quite a private dependency of\n[testdouble.js](https://github.com/testdouble/testdouble.js), as it\nimplements the `td.replace()` function's module-replacement behavior.\n\n## Usage\n\nSay we're testing pants:\n\n```js\nquibble = require('quibble')\n\ndescribe('pants', function(){\n  var subject, legs;\n  beforeEach(function(){\n    legs = quibble('./../lib/legs', function(){ return 'a leg';});\n\n    subject = require('./../lib/pants');\n  });\n  it('contains legs', function() {\n    expect(subject().left).toContain('a leg')\n    expect(subject().right).toContain('a leg')\n  })\n});\n```\n\nThat way, when the `subject` loaded from `lib/pants` runs `require('./legs')`,\nit will get back the function that returns `'a leg'`. The fake value is also\nreturned by `quibble`, which makes it easy to set and assign a test double in a\none-liner.\n\nFor more info on how this module is _really_ intended to be used, check out its\ninclusion in [testdouble.js](https://github.com/testdouble/testdouble.js/blob/main/docs/7-replacing-dependencies.md#nodejs)\n\n## Configuration\n\nThere's only one option: what you want to do with quibbled modules by default.\n\nSay you're pulling in [testdouble.js](https://github.com/testdouble/testdouble.js)\nand you want every quibbled module to default to a single test double function with\na name that matches its absolute path. You could do this:\n\n```js\nquibble = require('quibble')\nbeforeEach(function(){\n  quibble.config({\n    defaultFakeCreator: function(path) {\n      return require('testdouble').create(path);\n    }\n  });\n});\n```\n\nWith this set up, running `quibble('./some/path')` will default to replacing all\n`require('../anything/that/matches/some/path')` invocations with a test double named\nafter the absolute path resolved to by `'./some/path'`.\n\nSpiffy!\n\n\u003e Note: `defaultFakeCreator` is not supported for ES Module stubbing\n\n## ES Modules support\n\nQuibble supports ES Modules. Quibble implements ES module support using [ES Module\nLoaders](https://nodejs.org/api/esm.html#esm_experimental_loaders) which are the official way to\n\"patch\" Node.js' module loading mechanism for ESM.\n\n\u003e Note that Loader support is currently experimental and unstable. We are doing our best\n  to track the changes in the specification for the upcoming Node.js versions.\n\nIf you're running a Node.js version smaller than v20.6.0, you must run Node with the `quibble` package as a loader:\n\n```sh\nnode --loader=quibble ...\n```\n\nMost test runners allow you to specify this in their command line, e.g. for Mocha:\n\n```sh\nmocha --loader=quibble ...\n```\n\nThe `quibble` loader will enable the replacement of the ES modules with the stubs you specify, and\nwithout it, the stubbing will be ignored.\n\nFor versions larger or equal to v20.6.0, there is no need to specify a `--loader`, as registering the loader\nhappens automatically once you use the API.\n\n### Restrictions on ESM\n\n* `defaultFakeCreator` is not yet supported.\n\n### `quibble` ESM API\n\nThe API is similar to the CommonJS API, but uses `quibble.esm` function, and is async:\n\n```js\n// a-module.mjs (ESM)\nexport const life = 42;\nexport default 'universe';\n\n// uses-a-module.mjs\nimport universe, {life} from './a-module.mjs';\n\nconsole.log(life, universe);\n\n(async function () {\n  await quibble.esm('./a-module.mjs', {life: 41}, 'replacement universe');\n\n  await import('./uses-some-module.mjs');\n  // ==\u003e logs: 41, replacement universe\n})();\n```\n\nThe parameters to be given to `quibble.esm` for ESM modules are:\n\n1. the module path: similar to CommonJS, the path is relative to the directory you are in. It is\n   resolved the ESM way, so if you're using a relative path, you must specify the filename,\n   including the extension.\n2. the named export stubs: either null/undefied or an object with each property\n   having key corresponding to export names and value being the implementation\n   to use. To define the `default` export you can either define a `default`\n   property here or use the third argument, but not both at same time.\n3. the default export stub: if named export stubs does not contain a `default`\n   key, you can define the default stub with this argument.\n\nNote that `quibble.reset` works the same as for CommonJS modules\n\nESM support also exposes the function `quibble.esmImportWithPath` which both imports a module and\nresolves the path to the module that is the package's entry point:\n\n* `async quibble.esmImportWithPath(importPath)`: imports a module, just like `import(importPath)`,\n  but returns an object with two properties:\n  * `module`: the module returned by `await import(importPath)`.\n  * `modulePath`: the full path to the module (file) that is the entry point to the package/module.\n\n\u003e Note that when mocking internal Node.js modules (e.g. \"[fs](https://nodejs.org/api/fs.html)\")), you need to mock the named exports both as named exports and as properties in the default export, because Node.js exports internal modules both as named exports and as a default object. Example:\n\n```js\nconst fsExports = {\n  readFileSync: function (path) {\n    console.log(\"using quibbled readFileSyns... yay!\");\n    return \"Looks like 'fs' was replaced correctly.\";\n  },\n}\nawait quibble.esm(\"fs\", fsExports, fsExports);\n```\n\n## How's it different?\n\nA few things that stand out about quibble:\n\n1. No partial mocking, as proxyquire does. [Partial Mocks](https://github.com/testdouble/contributing-tests/wiki/Partial-Mock)\nare often seen problematic and not helpful for unit testing designed to create clear boundaries\nbetween the SUT and its dependencies\n2. Global replacements, so it's easy to set up a few arrange steps in advance of\ninstantiating your subject (using `require` just as you normally would). The instantiation\nstyle of other libs is a little different (e.g. `require('./my/subject', {'/this/thing': stub})`\n3. Require strings are resolved to absolute paths. It can be a bit confusing using other tools because from the perspective of the test particular paths are knocked out _from the perspective of the subject_ and not from the test listing, which runs counter to how every other Node.js API works. Instead, here, the path of the file being knocked out is relative to whoever is knocking it out.\n4. A configurable default faker function. This lib was written to support the [testdouble.js](https://github.com/testdouble/testdouble.js) feature [td.replace()](https://github.com/testdouble/testdouble.js/blob/main/docs/7-replacing-dependencies.md#nodejs), in an effort to reduce the amount of per-test friction to repetitively create \u0026 pass in test doubles\n5. A `reset()` method that undoes everything, intended to be run `afterEach` test runs\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdouble%2Fquibble","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftestdouble%2Fquibble","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdouble%2Fquibble/lists"}