{"id":13527297,"url":"https://github.com/node-modules/mm","last_synced_at":"2025-06-14T03:08:21.560Z","repository":{"id":5333328,"uuid":"6518669","full_name":"node-modules/mm","owner":"node-modules","description":"An simple but flexible mock(or say stub) package, mock mate","archived":false,"fork":false,"pushed_at":"2023-12-09T04:59:42.000Z","size":331,"stargazers_count":158,"open_issues_count":2,"forks_count":16,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-04-14T12:21:35.104Z","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":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/node-modules.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2012-11-03T11:14:34.000Z","updated_at":"2023-12-09T03:32:13.000Z","dependencies_parsed_at":"2024-01-13T23:10:07.300Z","dependency_job_id":"ff3b2566-8cd6-4020-ac6b-32fc5ade9f0a","html_url":"https://github.com/node-modules/mm","commit_stats":{"total_commits":144,"total_committers":13,"mean_commits":"11.076923076923077","dds":"0.45833333333333337","last_synced_commit":"fcb0e106a5636ebf48fd0ffd1890fe10294772b2"},"previous_names":[],"tags_count":59,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fmm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fmm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fmm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fmm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/node-modules","download_url":"https://codeload.github.com/node-modules/mm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246616080,"owners_count":20806050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:45.178Z","updated_at":"2025-04-01T09:31:22.129Z","avatar_url":"https://github.com/node-modules.png","language":"JavaScript","readme":"# mm, mock mate\n\n[![NPM version][npm-image]][npm-url]\n[![Node.js CI](https://github.com/node-modules/mm/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/mm/actions/workflows/nodejs.yml)\n[![Test coverage][codecov-image]][codecov-url]\n[![npm download][download-image]][download-url]\n[![Node.js Version](https://img.shields.io/node/v/mm.svg?style=flat)](https://nodejs.org/en/download/)\n\n[npm-image]: https://img.shields.io/npm/v/mm.svg?style=flat-square\n[npm-url]: https://npmjs.org/package/mm\n[codecov-image]: https://codecov.io/github/node-modules/mm/coverage.svg?branch=master\n[codecov-url]: https://codecov.io/github/node-modules/mm?branch=master\n[download-image]: https://img.shields.io/npm/dm/mm.svg?style=flat-square\n[download-url]: https://npmjs.org/package/mm\n\nAn simple but flexible **mock(or say stub)** package, mock mate.\n\n## Install\n\n```bash\nnpm install mm --save-dev\n```\n\n## Usage\n\n```ts\nimport fs from 'node:fs';\nimport { mm } from 'mm';\n\nmm(fs, 'readFileSync', function(filename) {\n  return filename + ' content';\n});\n\nconsole.log(fs.readFileSync('《九评 Java》'));\n// =\u003e 《九评 Java》 content\n\nrestore();\nconsole.log(fs.readFileSync('《九评 Java》'));\n// =\u003e throw `Error: ENOENT, no such file or directory '《九评 Java》`\n```\n\n### Support spy\n\nIf mocked property is a function, it will be spied, every time it called, mm will modify `.called`, `.calledArguments` and `.lastCalledArguments`. For example:\n\n```ts\nimport { mm } from 'mm';\n\nconst target = {\n  async add(a, b) {\n    return a + b;\n  },\n};\n\nmm.data(target, 'add', 3);\n\nassert.equal(await target.add(1, 1), 3);\nassert.equal(target.add.called, 1);\nassert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]);\nassert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]);\n\nassert.equal(await target.add(2, 2), 3);\nassert.equal(target.add.called, 2);\nassert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]);\nassert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);\n```\n\nIf you only need spy and don't need mock, you can use `mm.spy` method directly:\n\n```ts\nimport { mm } from 'mm';\n\nconst target = {\n  async add(a, b) {\n    await this.foo();\n    return a + b;\n  },\n  async foo() { /* */ },\n};\n\nmm.spy(target, 'add');\nassert.equal(await target.add(1, 1), 2);\nassert.equal(target.add.called, 1);\nassert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]);\nassert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]);\n\nassert.equal(await target.add(2, 2), 4);\nassert.equal(target.add.called, 2);\nassert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]);\nassert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);\n```\n\n## API\n\n### .error(module, propertyName, errerMessage, errorProperties)\n\n```ts\nimport fs from 'node:fs';\nimport { mm } from 'mm';\n\nmm.error(fs, 'readFile', 'mock fs.readFile return error');\n\nfs.readFile('/etc/hosts', 'utf8', function (err, content) {\n  // err.name === 'MockError'\n  // err.message === 'mock fs.readFile return error'\n  console.log(err);\n\n  mm.restore(); // remove all mock effects.\n\n  fs.readFile('/etc/hosts', 'utf8', function (err, content) {\n    console.log(err); // =\u003e null\n    console.log(content); // =\u003e your hosts\n  });\n});\n```\n\n### .errorOnce(module, propertyName, errerMessage, errorProperties)\n\nJust like `mm.error()`, but only mock error once.\n\n```ts\nimport fs from 'node:fs';\nimport { mm } from 'mm';\n\nmm.errorOnce(fs, 'readFile', 'mock fs.readFile return error');\n\nfs.readFile('/etc/hosts', 'utf8', function (err, content) {\n  // err.name === 'MockError'\n  // err.message === 'mock fs.readFile return error'\n  console.log(err);\n\n  fs.readFile('/etc/hosts', 'utf8', function (err, content) {\n    console.log(err); // =\u003e null\n    console.log(content); // =\u003e your hosts\n  });\n});\n```\n\n### .data(module, propertyName, secondCallbackArg)\n\n```js\nmm.data(fs, 'readFile', Buffer.from('some content'));\n\n// equals\n\nfs.readFile = function (...args, callback) {\n  callback(null, Buffer.from('some content'))\n};\n```\n\n### .dataWithAsyncDispose(module, propertyName, promiseResolveArg)\n\nSupport [Symbol.asyncDispose](https://www.totaltypescript.com/typescript-5-2-new-keyword-using)\n\n```js\nmm.dataWithAsyncDispose(locker, 'tryLock', {\n  locked: true,\n});\n\n// equals\n\nlocker.tryLock = async () =\u003e {\n  return {\n    locked: true,\n    [Symbol.asyncDispose](): async () =\u003e {\n      // do nothing\n    },\n  };\n}\n```\n\nRun test with `await using` should work:\n\n```js\nmm.dataWithAsyncDispose(locker, 'tryLock', {\n  locked: true,\n});\n\nawait using lock = await locker.tryLock('foo-key');\nassert.equal(lock.locked, true);\n```\n\n### .empty(module, propertyName)\n\n```js\nmm.empty(mysql, 'query');\n\n// equals\n\nmysql.query = function (...args, callback) {\n  callback();\n}\n```\n\n### .datas(module, propertyName, argsArray)\n\n```js\nmm.datas(urllib, 'request', [Buffer.from('data'), {headers: { foo: 'bar' }}]);\n\n// equals\n\nurllib.request = function (...args, callback) {\n  callback(null, Buffer.from('data'), {headers: { foo: 'bar' }});\n}\n```\n\n### .syncError(module, propertyName, errerMessage, errorProperties)\n\n```js\nvar { mm } = require('mm');\nvar fs = require('fs');\n\nmm.syncError(fs, 'readFileSync', 'mock fs.readFile return error', {code: 'ENOENT'});\n\n// equals\n\nfs.readFileSync = function (...args) {\n  var err = new Error('mock fs.readFile return error');\n  err.code = 'ENOENT';\n  throw err;\n};\n\n```\n\n### .syncData(module, propertyName, value)\n\n```js\nmm.syncData(fs, 'readFileSync', Buffer.from('some content'));\n\n// equals\n\nfs.readFileSync = function (...args) {\n  return Buffer.from('some content');\n};\n```\n\n### .syncEmpty\n\n```js\nmm.syncEmpty(fs, 'readFileSync');\n\n// equals\n\nfs.readFileSync = function (...args) {\n  return;\n}\n```\n\n### .restore()\n\n```js\n// restore all mock properties\nmm.restore();\n```\n\n### .http.request(mockUrl, mockResData, mockResHeaders) and .https.request(mockUrl, mockResData, mockResHeaders)\n\n```js\nvar { mm } = require('mm');\nvar http = require('http');\n\nvar mockURL = '/foo';\nvar mockResData = 'mock data';\nvar mockResHeaders = { server: 'mock server' };\nmm.http.request(mockURL, mockResData, mockResHeaders);\nmm.https.request(mockURL, mockResData, mockResHeaders);\n\n// http\nhttp.get({\n  path: '/foo'\n}, function (res) {\n  console.log(res.headers); // should be mock headers\n  var body = '';\n  res.on('data', function (chunk) {\n    body += chunk.toString();\n  });\n  res.on('end', function () {\n    console.log(body); // should equal 'mock data'\n  });\n});\n\n// https\nhttps.get({\n  path: '/foo'\n}, function (res) {\n  console.log(res.headers); // should be mock headers\n  var body = '';\n  res.on('data', function (chunk) {\n    body += chunk.toString();\n  });\n  res.on('end', function () {\n    console.log(body); // should equal 'mock data'\n  });\n});\n```\n\n### .http.requestError(mockUrl, reqError, resError) and .https.requestError(mockUrl, reqError, resError)\n\n```js\nvar { mm } = require('mm');\nvar http = require('http');\n\nvar mockURL = '/foo';\nvar reqError = null;\nvar resError = 'mock res error';\nmm.http.requestError(mockURL, reqError, resError);\n\nvar req = http.get({\n  path: '/foo'\n}, function (res) {\n  console.log(res.statusCode, res.headers); // 200 but never emit `end` event\n  res.on('end', fucntion () {\n    console.log('never show this message');\n  });\n});\nreq.on('error', function (err) {\n  console.log(err); // should return mock err: err.name === 'MockHttpResponseError'\n});\n```\n\n### .classMethod(instance, method, mockMethod)\n\n```js\nclass Foo {\n  async fetch() {\n    return 1;\n  }\n}\n\nconst foo = new Foo();\nconst foo1 = new Foo();\n\nmm.classMethod(foo, 'fetch', async () =\u003e {\n  return 3;\n});\nassert(await foo.fetch() === 3);\nassert(await foo1.fetch() === 3);\n```\n\n## License\n\n[MIT](LICENSE)\n\n## Contributors\n\n[![Contributors](https://contrib.rocks/image?repo=node-modules/mm)](https://github.com/node-modules/mm/graphs/contributors)\n\nMade with [contributors-img](https://contrib.rocks).\n","funding_links":[],"categories":["Repository"],"sub_categories":["Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-modules%2Fmm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnode-modules%2Fmm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-modules%2Fmm/lists"}