
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@lamnhan/testing
Advanced tools
Rewiring, mocking & helpers for testing modules in Node.
npm install --save-dev @lamnhan/testing
import { mockModule } from '@lamnhan/testing';
const mocked = mockModule({
a: () => 1,
b: async () => 2,
});
// test begins
Detail API reference at: https://lamnhan.com/testing
Module and mocked moduleA module is a dependency or a file that is imported by your code. Example: path module, module1 module, ...
import { resolve } from 'path';
import { something } from './module1';
There are 3 kinds of module:
node_modules/ dependenciessrc/ folderA mocked module is a module that was created to replace the original module for testing purpose.
Service and mocked serviceA service is an exported member of a module, usually an exported class in a module. Example: MyService service, AnotherService service, ...
import { MyService } from './module1';
import { AnotherService } from './module2';
A mocked service is a service that was created to replace the original service for testing purpose.
| Methods | Returns type | Description |
|---|---|---|
mockModule(members) | MockBuilder | Create a mock module |
mockService(members) | MockBuilder | Create a mock service |
rewireModule(loader, mockedModules) | ModuleRewiring | Rewire a service |
rewireService(serviceConstructor, mockedServices, withStubs) | ServiceRewiring | Rewire a service |
rewire(loader, mockedModules) | Rewiring | Unify api for rewiring both module & service |
rewireFull(loader, mockedModules, serviceInterface, mockedServices, withStubs) | FullRewiringResult | Shortcut to rewire(...).rewireFull(...) |
mockModule(members)Create a mock module for testing purpose, this method is a proxy to the MockBuilder.
The original module, for example:
function a() {
// do something
return 'something';
}
async function b() {
// do somthing else
return 'nothing at all';
}
export { a, b };
Create a mocked version of the original module:
const mockedModule = mockModule({
a: () => 1,
b: async () => 2,
});
// start using the mocked module
mockService(members)Create a mock service for testing purpose, this method is a proxy to the MockBuilder. See mockModule for usage info.
MockBuilderThe MockBuilder constructor create a mocked object for mocking modules and services.
const mocked = new MockBuilder({
a: () => 1,
b: async () => 2,
});
The MockBuilder create a mocked instance of any modules or services with every method defined in the members param. When a method is called, the mocked instance record all arguments and returns a value that defined by the members param.
These are the supported returns values.
*: returns this.: returns the first argumentFunction: returns the result of this function (with the same arguments as the original method)any: returns as is any other values: string, number, boolean, {}, any[], ...A MockBuilder instance provides these methods for retrieving testing data.
getAllReturns(): Get all the data holded by the Returns KeepergetAllArgs(): Get all the data holded by the Args KeepergetAllStackedArgs(): Get all the data holded by the StackedArgs KeepergetReturns(member): Get the raw value defined for a member to returngetReturnsResult(member): Get the returned value of a membergetArgs(member): Get a list of argsgetArg(member, position): Get an arg by paramter positiongetArgFirst(member): Get the first arggetArgSecond(member): Get the second arggetArgThird(member): Get the third arggetStackedArgs(member): Get a list of stacked argsgetStackedArgsChild(member, execution): Get a list of args by execution ordergetStackedArgsChildFirst(member): Get a list of args of the first executiongetStackedArgsChildSecond(member): Get a list of args of the second executiongetStackedArgsChildThird(member): Get a list of args of the third executiongetArgInStack(member, execution, position): Get an arg by execution order and parameter positiongetArgInStack1X1(member): Get the first arg of the first executiongetArgInStack1X2(member): Get the second arg of the first executiongetArgInStack1X3(member): Get the third arg of the first executiongetArgInStack2X1(member): Get the first arg of the second executiongetArgInStack2X2(member): Get the second arg of the second executiongetArgInStack2X3(member): Get the third arg of the second executiongetArgInStack3X1(member): Get the first arg of the third executiongetArgInStack3X2(member): Get the second arg of the third executiongetArgInStack3X3(member): Get the third arg of the third executionLoad modules or services with mocked dependencies.
A rewiring dependency is resolved by an ID, depending on the kind of a module:
id is the same as the name: path, os, ...id is the dependency name prefixed by a ~:
~lodash -> ./node_modules/lodash~@xxx/abc -> ./node_modules/xxx/abcid is prefixed by a @:
@src/xxx/abc -> ./src/xxx/abc@xxx/abc -> ./src/xxx/abcrewireModule(loader, mockedModules)Load a module with mocked dependencies.
// rewire the 'module1'
const module1Rewiring = rewireModule(
// load the original module
() => import('../src/module1'),
// (optional) replace dependencies with mocked instances
{
'path': {},
'~lodash': {},
'@xxx/abc': {},
}
);
// start test
it('ok', async () => {
// retrieve the rewired module
const rewiredModule1 = await module1Rewiring.getModule();
// test a method
const result = rewiredModule1.someMethod();
expect(result).equal('xxx');
});
ModuleRewiringModuleRewiring is the constructor of rewireModule, see rewireModule for the list of parameters.
| Method | Returns type | Description |
|---|---|---|
getMocked() | object | Get all mocked dependencies |
getModule() | Promise<object> | Get the rewired module |
getService(name) | Promise<class> | Get a service constructor of the mocked module |
rewireService(serviceConstructor, mockedServices, withStubs)Load a service with mocked dependencies and stubing methods.
import { MyService } from 'module1';
const myServiceRewiring = rewireService(
// the original or a mocked service constructor extracted from a rewired module
MyService,
// (optional) mocked service dependencies (constructor params)
{
'@xxx/abc': {},
'@xxx/xyz': {},
},
// (optional) pre-stubing methods
{
a: 1,
b: () => 2,
}
);
// start test
it('ok', async () => {
// retrieve the rewired service
const rewiredMyService = await myServiceRewiring.getInstance();
// test a method
const result = rewiredMyService.someMethod();
expect(result).equal('xxx');
});
ServiceRewiringServiceRewiring is the constructor of rewireService, see rewireService for the list of parameters.
| Method | Returns type | Description |
|---|---|---|
getInstance() | object | Get a instance of the rewired service |
getMocked() | object | Get all mocked dependencies |
stub(method) | sinon.SinonStub | Stub a method of the service |
setStubs(stubs) | ServiceRewiring | Stub multiple methods |
setStub(method, stubed) | ServiceRewiring | Stub a method |
getStubs() | object | Get all stubbed methods |
getStub(method) | sinon.SinonStub | Get a stubbed method |
restoreStubs() | ServiceRewiring | Restore all stubbed methods |
restoreStub(method) | ServiceRewiring | Restore a stubbed method |
rewire(loader, mockedModules)Unify api for rewiring both module & service.
RewiringRewiring is the constructor of rewire, see rewire for the list of parameters.
See rewireModule(), rewireService(...) and rewireFull(...) for more detail.
| Method | Returns type | Description |
|---|---|---|
rewireModule() | ModuleRewiring | Rewire a module |
rewireService(serviceInterface, mockedServices, withStubs) | ServiceRewiring | Rewire a service |
rewireFull(serviceInterface, mockedServices, withStubs) | Promise<FullRewiringResult> | Rewire module and service and return all data |
rewireFull(loader, mockedModules, serviceInterface, mockedServices, withStubs)The shortcut to rewire(...).rewireFull(...), resulting is a FullRewiringResult instance.
FullRewiringResultA FullRewiringResult instance provides properties/methods to retrieve data for testing.
| Prop/method | Returns type | Description |
|---|---|---|
moduleRewiring | ModuleRewiring | The module rewiring instance |
mockedModules | object | All mocked modules |
serviceName | string | The rewired service name |
serviceRewiring | ServiceRewiring | The service rewiring instance |
mockedServices | object | All mocked services |
service | object | The rewired service instance |
getModuleRewiring() | ModuleRewiring | Get the module rewiring instance |
getMockedModules() | object | Get all mocked modules |
getMockedModule(id) | object | Get a mocked module |
getServiceName() | string | Get the rewired service name |
getServiceRewiring() | ServiceRewiring | Get the service rewiring instance |
getMockedServices() | object | Get all mocked services |
getMockedService(id) | object | Get a mocked service |
getService | object | Get the rewired service instance |
All testing examples is using mocha as test runner and chai as assertion tool.
An example of how to create a mocked version of a module or a service.
./src/module1.ts
export function doSomething1() {
// do something
return 'something';
}
export async function doSomething2() {
// do somthing else
return 'nothing at all';
}
./test/module1.spec.ts
const mockedModule = mockModule({
doSomething1: 'any mocked returns value',
doSomething2: async () => 'any mocked returns value',
});
// start using the mocked module
An example of how to rewire a module.
./src/module1.ts
import { resolve } from 'path';
export function doSomething() {
const useExternal = resolve('xxx');
return 'something';
}
./test/module1.spec.ts
import { rewireModule } from '@lamnhan/testing';
// setup
function getModule() {
return rewireModule(
// load the tested module
() => import('../src/module1'),
// rewire all dependencies with mocked replacement
{
'path': {
resolve: () => 'any mocked returns value',
// mock other methods of this module
},
}
);
}
// start testing
describe('Test module1', () => {
it('#doSomething', async () => {
// retrieve the rewired module
const module1Rewiring = getModule();
const rewiredModule1 = await module1Rewiring.getModule();
// test a module member
const result = rewiredModule1.doSomething();
expect(result).equal('xxx');
});
});
An example of how to rewire a service.
./src/module1.ts
export class Service1 {
constructor() {}
doSomething(param1: any) {
// do something with the 'param1'
return 'something';
}
}
./src/module2.ts
import { Service1 } from './module1';
export class Service2 {
private service1: Service1;
constructor(service1: Service1) {
this.service1 = service1;
}
doSomething() {
const useExternal = this.service1.doSomething('xxx');
return 'something';
}
}
./test/module2.spec.ts
import { rewireService } from '@lamnhan/testing';
import { Service2 } from '../src/module2';
// setup
function getService() {
return rewireService(
// rewire this service
Service2,
// replace all dependencies with mocked replacement
{
'@src/module1': {
doSomething1: 'any mocked returns value';
},
},
// no stubbing for now
);
}
// start testing
describe('Test Service2', () => {
it('#doSomething2', async () => {
// retrieve the rewired service
const service2Rewiring = getService();
const rewiredService2 = await service2Rewiring.getInstance();
// retrieve a mocked servics for passed argument testing
const { '@src/module1': mockedModule1 } = service2Rewiring.getMocked();
// test a module member
const result = rewiredService2.doSomething();
expect(result).equal('...');
expect(
mockedModule1.getArgFirst('doSomething'), // get the first arg of #doSomething
).equal('xxx');
});
});
An example of how to rewire a module and a service with full functionality.
./src/module1.ts
import { resolve } from 'path';
import { readFile } from 'fs-extra';
import { Service2 } from './service2';
export class MyService {
private service2: Service2;
constructor(service2: Service2) {
this.service2 = service2;
}
doSomething() {
const usePath = resolve('xxx');
const useFSExtra = readFile('xxx.txt');
const useService2 = this.service2.doSomething('xxx');
return 'something';
}
doMore() {
const useThis = this.doSomething();
return 'do more';
}
}
./test/module1.spec.ts
import { rewireFull } from '@lamnhan/testing';
import { MyService } from '../src/module1';
// setup test
async function setup(
stubs: any,
) {
return rewireFull(
// load the tested module
() => import('../src/module1'),
// rewire all dependencies with mocked replacement
{
'path': {
resolve: () => 'any mocked returns value',
// mock other methods of this module
},
'~fs-extra': {
readFile: async () => 'any mocked returns value',
// mock other methods of this module
}
}
// rewire this service
MyService,
// replace all dependencies with mocked replacement
{
'@src/service2': {
doSomething: 'any mocked returns value';
},
},
stubs,
);
}
// start testing
describe('Test MyService', () => {
it('#doSomething', async () => {
// retrieve the data
const {
service,
mockedModules: {
'path': mockedPathModuleTesting,
'~fs-extra': mockedFSExtraModuleTesting,
},
mockedServices: {
'@src/service2': mockedService2Testing,
}
} = await setup();
// test a service method
const result = service.doSomething();
expect(result).equal('...');
// do more assertions about passed arguments
const resolveArgs = mockedPathModuleTesting.getArgFirst('resolve');
const readFileArg = mockedFSExtraModuleTesting.getArgFirst('readFile');
const doSomethingArg = mockedService2Testing.getArgFirst('doSomething');
expect(resolveArg).equal('xxx');
expect(readFileArg).equal('xxx.txt');
expect(doSomethingArg).equal('xxx');
// ...
});
it('#doMore', async () => {
// retrieve the data
const { service } = await setup({
doSomething: 'returns something else',
});
// test a service method
const result = service.doMore();
expect(result).equal('...');
});
});
@lamnhan/testing is released under the MIT license.
FAQs
Rewiring, mocking & stubbing for testing modules in Node.
The npm package @lamnhan/testing receives a total of 2 weekly downloads. As such, @lamnhan/testing popularity was classified as not popular.
We found that @lamnhan/testing demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.