Stub require in node js - node.js

Is it any way to stub require in node js using sinon.js?
For example, i have some file:
const myModule = require('awesomeModule');
And I want to have such stub in my tests:
myRequireStub.withArgs('awesomModule').throws(new Error('some error'));

This works:
const sinon = require('sinon');
let myRequireStub = sinon.stub(module, 'require');
myRequireStub.withArgs('awesomeModule').throws(new Error('some error'));
const myModule = require('awesomeModule');
// Error: some error
However, by stubbing require you'll pretty much break the ability to load modules, so I'm not sure how useful this is.

Depending on your usage, it's probably best to use a library such as mockery or mock-require. But I needed a way to stub all calls to require, so this is what I came up with:
const _module = require('module');
let require = sinon.stub(_module, '_load');
//return a function that returns a function
require.returns(() => { return () => {}; });
//run code that calls require
require.restore();
assert(require.calledWith('module-name'));

Related

Testing async methods using Mocha, Chai, node.js

I have a very simple code structure like this
TestWorks.ts
const axios = require('axios');
export class TestWorks{
async getUsersList(param1:TestModel, userDetail:any){
console.log("BEGIN -- ... ");
And then this is my test class
MyTest.ts
const testworks = require("../src/interfaces/TestService/TestWorks");
it('Get Users', async () => {
var x = await testworks.getUsersList({}, {});
expect(x).to.be.an("object");
});
but I am seeing the following error, unable to figure out what the issue could be. The paths are definitely right, not an issue with the file paths of where the files are
Get Users:
TypeError: testworks.getUsersList is not a function
at C:\Users\xxxxxx\Documents\xxxxx\test\test-server.test.ts:53:28
testworks refers to the module (or whatever TypeScript exports) because you use require(). You should use import for TypeScript modules.
import { TestWorks } from '../src/interfaces/TestService/TestWorks';

Assert that the function passed into a stubbed function is the correct function

I'm trying to test my Node module using Mocha.
The module is very small here is an example...
import { sharedFunctionA, sharedFunctionB, commonFunction } from <file>
const functionA = token => _data => sharedFunctionA(token);
const functionB = () => data => sharedFunctionB(data);
exports.doThingA = token => {
commonFunction(functionA(token));
};
exports.doThingB = () => {
commonFunction(functionB());
};
This is only a brief example but it shows what I'm trying to do.
I need to test that doThingA and doThingB pass in the correct function to the commonFunction.
I have stubbed the commonFunction and I can see that it is being called but I can't assert that the function passed in is correct.
TBH... I'm beginning to think of restructuring this entirely to pass in some sort of enum to the commonFunction and running the respective function from there.
In this case you can stub on the sharedFunctionA and sharedFunctionB and then retrieve the argument of your stub on the commonFunction and call it. Then check your other stubs are being called with the desired arguments.
I know it's tedious but it is the only way I can think of with your code.
Quick example:
const assert = require('assert')
const sinon = require('sinon')
const sharedFunctions = require('<fileWithSharedFunctions>')
const commonStub = sinon.stub(sharedFunctions, 'commonFunction')
const sharedBStub = sinon.stub(sharedFunctions, 'sharedFunctionB')
const fileToTest = require('<fileToTest>')
fileToTest.doThingB()
commonStub.getCall(0).args[0]()
assert(sharedBStub.calledOnce)

How does jest allow mutation of modules?

In this question that I asked here:
Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?
I'm asking about the nature of module mutation.
However as it it turns out, ES6 modules can't actually be mutated - all of their properties are treated as constants. (See this answer)
But somehow - when Jest tests modules - they can be mutated, and that's how Jest allows for mocking.
How is this happening?
I imagine that it's a babel plugin that that's running - transpiling the module to CommonJS modules? Is there any documentation about this?
Is there a way to view the transpiled code?
ES6 modules can't actually be mutated - all of their properties are treated as constants.
Interesting. You're right, even something as simple as this:
import * as lib from "./lib"; // import an ES6 module
const spy = jest.spyOn(lib, 'someFunc'); // spy on someFunc
...technically shouldn't be allowed since jest.spyOn replaces the method on the object with a spy and lib.someFunc should be a binding to someFunc in the ES6 module.
But somehow - when Jest tests modules - they can be mutated, and that's how Jest allows for mocking.
How is this happening?
They can only be mutated because Jest isn't actually using ES6 modules.
(I guess for completeness it might be possible to run Jest using actual ES6 modules by using Node's experimental support for ES6 Modules but I haven't tried).
I imagine that it's a babel plugin that that's running - transpiling the module...Is there any documentation about this?
"babel-jest is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the transform configuration option".
So by default Jest will use babel-jest which transpiles the source code using babel (and does a few other things like hoisting calls to jest.mock).
Note that Jest can be also be configured using transform which maps "regular expressions to paths to transformers".
Is there a way to view the transpiled code?
Yes. Transformations are done in jest-runtime here and the output is saved to a cache here.
The easiest way to look at the transpiled code is to view the cache.
You can do that by running Jest with the --showConfig option which will output the config used when running Jest. The cache location can be found by looking at the value of "cacheDirectory".
Then run Jest with the --clearCache option to clear out the cache.
Finally, run Jest normally and the cache directory will contain the transpiled code for your project.
Example
The latest Jest (v24) will transpile this code:
// lib.js
export const someFunc = () => 1;
// code.js
import { someFunc } from './lib';
export const func = () => someFunc() + 1;
// code.test.js
import { func } from './code';
import * as lib from './lib';
test('func', () => {
const spy = jest.spyOn(lib, 'someFunc');
func();
expect(spy).toHaveBeenCalled(); // SUCCESS
});
...to this:
// lib.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.someFunc = void 0;
const someFunc = () => 1;
exports.someFunc = someFunc;
// code.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.func = void 0;
var _lib = require("./lib");
const func = () => (0, _lib.someFunc)() + 1;
exports.func = func;
// code.test.js
"use strict";
var _code = require("./code");
var lib = _interopRequireWildcard(require("./lib"));
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
test('func', () => {
const spy = jest.spyOn(lib, 'someFunc');
(0, _code.func)();
expect(spy).toHaveBeenCalled(); // SUCCESS
});
The import * as lib from 'lib'; line gets handled by _interopRequireWildcard which uses require under the hood.
Every call to require "will get exactly the same object returned, if it would resolve to the same file" so code.js and code.test.js are getting the same object from require('./lib').
someFunc is exported as exports.someFunc which allows it to be reassigned.
So yes, you're exactly right. Spying (or mocking) like this only works because the ES6 modules are getting transpiled by babel into Node modules in a way that allows them to be mutated.

Can I spy with sinon a function call in an external module function?

I have a module:
let xmlParser = require('./myTools').xmlParser;
function extractDataAndWrite(xmldata) {
let doc = xmlParser(xmldata);
...
}
module.exports = {
extractDataAndWrite,
};
now I want test the call of xmlParser in extractDataAndWrite:
var extractDataAndWrite = require('../services/importData.js').extractDataAndWrite;
var mytools = require('./myTools');
var sinon = require('sinon');
describe('Test extractDataAndWrite', function() {
it('call xmlParser', function(done) {
var xmlParserSpy = sinon.spy(mytools, 'xmlParser');
extractDataAndWrite("someXML");
console.log('xmlParserSpy: ' + xmlParserSpy.callCount);
done();
});
});
I expect get xmlParserSpy.callCount == 1 but it is 0!
My spy is not working, what must I change?
The problem is that when you create a spy on a function, you are replacing that function reference with a new function. That means that people that reference the old function will not use the new function. In your case, things are not working, as you wrap the exported function reference in mytools' after your own module has already referenced the old function reference.
The general techniques you need to look into are called dependency injection and link seams. The Sinon docs has a good tutorial on the latter, using proxyquire.
Essentially you would have this:
const proxyquire = require('proxyquire');
const toolsStub = createToolsStub();
const importData = proxyquire('../services/importData.js', {
'./myTools': toolsStub
});
function createToolsStub(){
return { xmlParser : sinon.stub().returns({mydoc:{foo:'bar'}};
}
Then later on in your test you could check the xmlParser for calls
assert(toolsStub.xmlParser.calledWith('arg1', 'arg2');

nodeJS proxyquire overwrite specific function of a require

I am currently testing a module in isolation using proxquire to overwrite a require of this module.
Overwriting a path of a require works fine with proxyquire. For example:
var bar = require('./bar');
But can you use proxyquire also to overwrite just a specific function of a module which is required in the module to test? So something like:
var bar = require('./foo').bar();
I need to stay at proxyquire for this since I am using it for mocking a http-request happening in another layer of the architecture. But in case of the test I need to mock the time for "now" in the module as well.
So currently I have this:
var uraStub = sendMockRequest(paramListOfCheckin, queryList);
var setNowStub = function(){ return 1425998221000; };
var checkin = proxyquire('../src/logic/logicHandlerModules/checkin', {
'../../persistence/ura' : uraStub,
'./checkin.setNow' : setNowStub
});
checkin.checkin(...)
The implementation of setNow is:
var setNow = function(){
return new Date().getTime();
};
var checkin = function (...) {
var now = require('./checkin').setNow();
Obviousley './checkin.setNow' : setNowStub in proxyquire doesn't work, since this is the wrong path. But using './checkin'.setNow() : setNowStub also doesn't work because of wrong syntaxis in the object-definition.
Any suggestions?
Thanks in advance!
What you are looking for is the noCallThru() and callThru() methods. https://github.com/thlorenz/proxyquire#preventing-call-thru-to-original-dependency
By default proxyRequire will call through to the mocked dependency which will allow you to pick the methods that you want to overwrite with your own custom function.
So if a dependency in the path '../foo' has a method bar() and fooBar() you would be able to mock out just bar by doing.
proxyquire.callThru();
var fooFunc = proxyquire('../foo', {
bar: () => return 'bar'
})
Now bar() will hit your custom overwritten funtion while fooBar() will be called as normal.

Resources