https://github.com/node-modules/mm
An simple but flexible mock(or say stub) package, mock mate
https://github.com/node-modules/mm
Last synced: about 1 month ago
JSON representation
An simple but flexible mock(or say stub) package, mock mate
- Host: GitHub
- URL: https://github.com/node-modules/mm
- Owner: node-modules
- License: other
- Created: 2012-11-03T11:14:34.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2023-12-09T04:59:42.000Z (over 1 year ago)
- Last Synced: 2024-04-14T12:21:35.104Z (about 1 year ago)
- Language: JavaScript
- Homepage:
- Size: 323 KB
- Stars: 158
- Watchers: 18
- Forks: 16
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-nodejs - mm - An simple but flexible mock(or say stub) package, mock mate.  (Repository / Testing)
README
# mm, mock mate
[![NPM version][npm-image]][npm-url]
[](https://github.com/node-modules/mm/actions/workflows/nodejs.yml)
[![Test coverage][codecov-image]][codecov-url]
[![npm download][download-image]][download-url]
[](https://nodejs.org/en/download/)[npm-image]: https://img.shields.io/npm/v/mm.svg?style=flat-square
[npm-url]: https://npmjs.org/package/mm
[codecov-image]: https://codecov.io/github/node-modules/mm/coverage.svg?branch=master
[codecov-url]: https://codecov.io/github/node-modules/mm?branch=master
[download-image]: https://img.shields.io/npm/dm/mm.svg?style=flat-square
[download-url]: https://npmjs.org/package/mmAn simple but flexible **mock(or say stub)** package, mock mate.
## Install
```bash
npm install mm --save-dev
```## Usage
```ts
import fs from 'node:fs';
import { mm } from 'mm';mm(fs, 'readFileSync', function(filename) {
return filename + ' content';
});console.log(fs.readFileSync('《九评 Java》'));
// => 《九评 Java》 contentrestore();
console.log(fs.readFileSync('《九评 Java》'));
// => 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:
```ts
import { mm } from 'mm';const target = {
async add(a, b) {
return a + b;
},
};mm.data(target, 'add', 3);
assert.equal(await target.add(1, 1), 3);
assert.equal(target.add.called, 1);
assert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]);
assert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]);assert.equal(await target.add(2, 2), 3);
assert.equal(target.add.called, 2);
assert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]);
assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);
```If you only need spy and don't need mock, you can use `mm.spy` method directly:
```ts
import { mm } from 'mm';const target = {
async add(a, b) {
await this.foo();
return a + b;
},
async foo() { /* */ },
};mm.spy(target, 'add');
assert.equal(await target.add(1, 1), 2);
assert.equal(target.add.called, 1);
assert.deepEqual(target.add.calledArguments, [[ 1, 1 ]]);
assert.deepEqual(target.add.lastCalledArguments, [ 1, 1 ]);assert.equal(await target.add(2, 2), 4);
assert.equal(target.add.called, 2);
assert.deepEqual(target.add.calledArguments, [[ 1, 1 ], [ 2, 2 ]]);
assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]);
```## API
### .error(module, propertyName, errerMessage, errorProperties)
```ts
import fs from 'node:fs';
import { mm } from 'mm';mm.error(fs, 'readFile', 'mock fs.readFile return error');
fs.readFile('/etc/hosts', 'utf8', function (err, content) {
// err.name === 'MockError'
// err.message === 'mock fs.readFile return error'
console.log(err);mm.restore(); // remove all mock effects.
fs.readFile('/etc/hosts', 'utf8', function (err, content) {
console.log(err); // => null
console.log(content); // => your hosts
});
});
```### .errorOnce(module, propertyName, errerMessage, errorProperties)
Just like `mm.error()`, but only mock error once.
```ts
import fs from 'node:fs';
import { mm } from 'mm';mm.errorOnce(fs, 'readFile', 'mock fs.readFile return error');
fs.readFile('/etc/hosts', 'utf8', function (err, content) {
// err.name === 'MockError'
// err.message === 'mock fs.readFile return error'
console.log(err);fs.readFile('/etc/hosts', 'utf8', function (err, content) {
console.log(err); // => null
console.log(content); // => your hosts
});
});
```### .data(module, propertyName, secondCallbackArg)
```js
mm.data(fs, 'readFile', Buffer.from('some content'));// equals
fs.readFile = function (...args, callback) {
callback(null, Buffer.from('some content'))
};
```### .dataWithAsyncDispose(module, propertyName, promiseResolveArg)
Support [Symbol.asyncDispose](https://www.totaltypescript.com/typescript-5-2-new-keyword-using)
```js
mm.dataWithAsyncDispose(locker, 'tryLock', {
locked: true,
});// equals
locker.tryLock = async () => {
return {
locked: true,
[Symbol.asyncDispose](): async () => {
// do nothing
},
};
}
```Run test with `await using` should work:
```js
mm.dataWithAsyncDispose(locker, 'tryLock', {
locked: true,
});await using lock = await locker.tryLock('foo-key');
assert.equal(lock.locked, true);
```### .empty(module, propertyName)
```js
mm.empty(mysql, 'query');// equals
mysql.query = function (...args, callback) {
callback();
}
```### .datas(module, propertyName, argsArray)
```js
mm.datas(urllib, 'request', [Buffer.from('data'), {headers: { foo: 'bar' }}]);// equals
urllib.request = function (...args, callback) {
callback(null, Buffer.from('data'), {headers: { foo: 'bar' }});
}
```### .syncError(module, propertyName, errerMessage, errorProperties)
```js
var { mm } = require('mm');
var fs = require('fs');mm.syncError(fs, 'readFileSync', 'mock fs.readFile return error', {code: 'ENOENT'});
// equals
fs.readFileSync = function (...args) {
var err = new Error('mock fs.readFile return error');
err.code = 'ENOENT';
throw err;
};```
### .syncData(module, propertyName, value)
```js
mm.syncData(fs, 'readFileSync', Buffer.from('some content'));// equals
fs.readFileSync = function (...args) {
return Buffer.from('some content');
};
```### .syncEmpty
```js
mm.syncEmpty(fs, 'readFileSync');// equals
fs.readFileSync = function (...args) {
return;
}
```### .restore()
```js
// restore all mock properties
mm.restore();
```### .http.request(mockUrl, mockResData, mockResHeaders) and .https.request(mockUrl, mockResData, mockResHeaders)
```js
var { mm } = require('mm');
var http = require('http');var mockURL = '/foo';
var mockResData = 'mock data';
var mockResHeaders = { server: 'mock server' };
mm.http.request(mockURL, mockResData, mockResHeaders);
mm.https.request(mockURL, mockResData, mockResHeaders);// http
http.get({
path: '/foo'
}, function (res) {
console.log(res.headers); // should be mock headers
var body = '';
res.on('data', function (chunk) {
body += chunk.toString();
});
res.on('end', function () {
console.log(body); // should equal 'mock data'
});
});// https
https.get({
path: '/foo'
}, function (res) {
console.log(res.headers); // should be mock headers
var body = '';
res.on('data', function (chunk) {
body += chunk.toString();
});
res.on('end', function () {
console.log(body); // should equal 'mock data'
});
});
```### .http.requestError(mockUrl, reqError, resError) and .https.requestError(mockUrl, reqError, resError)
```js
var { mm } = require('mm');
var http = require('http');var mockURL = '/foo';
var reqError = null;
var resError = 'mock res error';
mm.http.requestError(mockURL, reqError, resError);var req = http.get({
path: '/foo'
}, function (res) {
console.log(res.statusCode, res.headers); // 200 but never emit `end` event
res.on('end', fucntion () {
console.log('never show this message');
});
});
req.on('error', function (err) {
console.log(err); // should return mock err: err.name === 'MockHttpResponseError'
});
```### .classMethod(instance, method, mockMethod)
```js
class Foo {
async fetch() {
return 1;
}
}const foo = new Foo();
const foo1 = new Foo();mm.classMethod(foo, 'fetch', async () => {
return 3;
});
assert(await foo.fetch() === 3);
assert(await foo1.fetch() === 3);
```## License
[MIT](LICENSE)
## Contributors
[](https://github.com/node-modules/mm/graphs/contributors)
Made with [contributors-img](https://contrib.rocks).