Installations
npm install mm
Developer Guide
Typescript
Yes
Module System
ESM
Min. Node Version
>= 18.19.0
Node Version
18.20.5
NPM Version
10.8.2
Releases
Contributors
Unable to fetch Contributors
Languages
TypeScript (100%)
Developer
node-modules
Download Statistics
Total Downloads
6,114,141
Last Day
2,284
Last Week
14,829
Last Month
83,663
Last Year
1,096,627
GitHub Statistics
159 Stars
173 Commits
16 Forks
19 Watching
4 Branches
25 Contributors
Package Meta Information
Latest Version
4.0.2
Package Id
mm@4.0.2
Unpacked Size
120.06 kB
Size
24.47 kB
File Count
11
NPM Version
10.8.2
Node Version
18.20.5
Publised On
02 Jan 2025
Total Downloads
Cumulative downloads
Total Downloads
6,114,141
Last day
-20.7%
2,284
Compared to previous day
Last week
-26.9%
14,829
Compared to previous week
Last month
-1.6%
83,663
Compared to previous month
Last year
-1.5%
1,096,627
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
mm, mock mate
An simple but flexible mock(or say stub) package, mock mate.
Install
1npm install mm --save-dev
Usage
1import fs from 'node:fs'; 2import { mm } from 'mm'; 3 4mm(fs, 'readFileSync', function(filename) { 5 return filename + ' content'; 6}); 7 8console.log(fs.readFileSync('《九评 Java》')); 9// => 《九评 Java》 content 10 11restore(); 12console.log(fs.readFileSync('《九评 Java》')); 13// => throw `Error: ENOENT, no such file or directory '《九评 Java》`
Support spy
If mocked property is a function, it will be spied, every time it called, mm will modify .called
, .calledArguments
and .lastCalledArguments
. For example:
1import { mm } from 'mm'; 2 3const target = { 4 async add(a, b) { 5 return a + b; 6 }, 7}; 8 9mm.data(target, 'add', 3); 10 11assert.equal(await target.add(1, 1), 3); 12assert.equal(target.add.called, 1); 13assert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]); 14assert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]); 15 16assert.equal(await target.add(2, 2), 3); 17assert.equal(target.add.called, 2); 18assert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]); 19assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);
If you only need spy and don't need mock, you can use mm.spy
method directly:
1import { mm } from 'mm'; 2 3const target = { 4 async add(a, b) { 5 await this.foo(); 6 return a + b; 7 }, 8 async foo() { /* */ }, 9}; 10 11mm.spy(target, 'add'); 12assert.equal(await target.add(1, 1), 2); 13assert.equal(target.add.called, 1); 14assert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]); 15assert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]); 16 17assert.equal(await target.add(2, 2), 4); 18assert.equal(target.add.called, 2); 19assert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]); 20assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);
API
.error(module, propertyName, errerMessage, errorProperties)
1import fs from 'node:fs'; 2import { mm } from 'mm'; 3 4mm.error(fs, 'readFile', 'mock fs.readFile return error'); 5 6fs.readFile('/etc/hosts', 'utf8', function (err, content) { 7 // err.name === 'MockError' 8 // err.message === 'mock fs.readFile return error' 9 console.log(err); 10 11 mm.restore(); // remove all mock effects. 12 13 fs.readFile('/etc/hosts', 'utf8', function (err, content) { 14 console.log(err); // => null 15 console.log(content); // => your hosts 16 }); 17});
.errorOnce(module, propertyName, errerMessage, errorProperties)
Just like mm.error()
, but only mock error once.
1import fs from 'node:fs'; 2import { mm } from 'mm'; 3 4mm.errorOnce(fs, 'readFile', 'mock fs.readFile return error'); 5 6fs.readFile('/etc/hosts', 'utf8', function (err, content) { 7 // err.name === 'MockError' 8 // err.message === 'mock fs.readFile return error' 9 console.log(err); 10 11 fs.readFile('/etc/hosts', 'utf8', function (err, content) { 12 console.log(err); // => null 13 console.log(content); // => your hosts 14 }); 15});
.data(module, propertyName, secondCallbackArg)
1mm.data(fs, 'readFile', Buffer.from('some content'));
2
3// equals
4
5fs.readFile = function (...args, callback) {
6 callback(null, Buffer.from('some content'))
7};
.dataWithAsyncDispose(module, propertyName, promiseResolveArg)
Support Symbol.asyncDispose
1mm.dataWithAsyncDispose(locker, 'tryLock', { 2 locked: true, 3}); 4 5// equals 6 7locker.tryLock = async () => { 8 return { 9 locked: true, 10 [Symbol.asyncDispose](): async () => { 11 // do nothing 12 }, 13 }; 14}
Run test with await using
should work:
1mm.dataWithAsyncDispose(locker, 'tryLock', {
2 locked: true,
3});
4
5await using lock = await locker.tryLock('foo-key');
6assert.equal(lock.locked, true);
.empty(module, propertyName)
1mm.empty(mysql, 'query');
2
3// equals
4
5mysql.query = function (...args, callback) {
6 callback();
7}
.datas(module, propertyName, argsArray)
1mm.datas(urllib, 'request', [Buffer.from('data'), {headers: { foo: 'bar' }}]);
2
3// equals
4
5urllib.request = function (...args, callback) {
6 callback(null, Buffer.from('data'), {headers: { foo: 'bar' }});
7}
.syncError(module, propertyName, errerMessage, errorProperties)
1var { mm } = require('mm');
2var fs = require('fs');
3
4mm.syncError(fs, 'readFileSync', 'mock fs.readFile return error', {code: 'ENOENT'});
5
6// equals
7
8fs.readFileSync = function (...args) {
9 var err = new Error('mock fs.readFile return error');
10 err.code = 'ENOENT';
11 throw err;
12};
13
.syncData(module, propertyName, value)
1mm.syncData(fs, 'readFileSync', Buffer.from('some content'));
2
3// equals
4
5fs.readFileSync = function (...args) {
6 return Buffer.from('some content');
7};
.syncEmpty
1mm.syncEmpty(fs, 'readFileSync');
2
3// equals
4
5fs.readFileSync = function (...args) {
6 return;
7}
.restore()
1// restore all mock properties 2mm.restore();
.http.request(mockUrl, mockResData, mockResHeaders) and .https.request(mockUrl, mockResData, mockResHeaders)
1var { mm } = require('mm'); 2var http = require('http'); 3 4var mockURL = '/foo'; 5var mockResData = 'mock data'; 6var mockResHeaders = { server: 'mock server' }; 7mm.http.request(mockURL, mockResData, mockResHeaders); 8mm.https.request(mockURL, mockResData, mockResHeaders); 9 10// http 11http.get({ 12 path: '/foo' 13}, function (res) { 14 console.log(res.headers); // should be mock headers 15 var body = ''; 16 res.on('data', function (chunk) { 17 body += chunk.toString(); 18 }); 19 res.on('end', function () { 20 console.log(body); // should equal 'mock data' 21 }); 22}); 23 24// https 25https.get({ 26 path: '/foo' 27}, function (res) { 28 console.log(res.headers); // should be mock headers 29 var body = ''; 30 res.on('data', function (chunk) { 31 body += chunk.toString(); 32 }); 33 res.on('end', function () { 34 console.log(body); // should equal 'mock data' 35 }); 36});
.http.requestError(mockUrl, reqError, resError) and .https.requestError(mockUrl, reqError, resError)
1var { mm } = require('mm'); 2var http = require('http'); 3 4var mockURL = '/foo'; 5var reqError = null; 6var resError = 'mock res error'; 7mm.http.requestError(mockURL, reqError, resError); 8 9var req = http.get({ 10 path: '/foo' 11}, function (res) { 12 console.log(res.statusCode, res.headers); // 200 but never emit `end` event 13 res.on('end', fucntion () { 14 console.log('never show this message'); 15 }); 16}); 17req.on('error', function (err) { 18 console.log(err); // should return mock err: err.name === 'MockHttpResponseError' 19});
.classMethod(instance, method, mockMethod)
1class Foo { 2 async fetch() { 3 return 1; 4 } 5} 6 7const foo = new Foo(); 8const foo1 = new Foo(); 9 10mm.classMethod(foo, 'fetch', async () => { 11 return 3; 12}); 13assert(await foo.fetch() === 3); 14assert(await foo1.fetch() === 3);
License
Contributors
Made with contributors-img.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Warn: project license file does not contain an FSF or OSI license.
Reason
6 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5
Reason
Found 5/30 approved changesets -- score normalized to 1
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pkg.pr.new.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/node-modules/mm/pkg.pr.new.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pkg.pr.new.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/node-modules/mm/pkg.pr.new.yml/master?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/pkg.pr.new.yml:19
- Info: 0 out of 2 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 1 npmCommand dependencies pinned
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1
- Warn: no topLevel permission defined: .github/workflows/pkg.pr.new.yml:1
- Warn: no topLevel permission defined: .github/workflows/release.yml:1
- Info: no jobLevel write permissions found
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
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
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 14 are checked with a SAST tool
Score
4.4
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreGathering detailed insights and metrics for mm