Does Rewire set the current running module variables as well as the specified module variables? - node.js

So, I was doing some testing with Rewire with Mocha, and I noticed behavior that seems odd to me. using myModule.__set__() seems to actually set the specified variable in the the specified module (myModule), AND in the global scope of the current module (the one that ran the __set__()). For example:
This code runs after running mocha test:
Test.js:
var rewire = require("rewire")
var sinon = require("sinon")
var test2 = rewire("./test2.js")
var expect = require("chai").expect
var spy = sinon.spy()
describe("test", function () {
beforeEach(function () {
test2.__set__("console", { log: spy })
})
it("test should be equal to 3", function () {
test2.test1()
console.log("testing5")
expect(spy.callCount).to.equal(3)
})
})
Test2.js:
module.exports = {
test1() {
console.log("test")
console.log("test")
console.log("test")
}
}
Here, I would expect "testing5" to actually be logged to the console, but the 3 "test"s to just be recorded in the spy, and the callCount to be equal to 3. This is not what is happening however. The "testing5" is not being logged, but is being recorded to that sinon spy, and the test is failing, because the callCount is 4, and not 3. To me this doesn't seem like this would be intended. Is there something I'm doing wrong?

Related

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.

Why can't I see console log output when testing node apps with jest

I am new to testing with jest and I cannot seem to be able to see the console output from modules I want to test.
my-module.js:
var _ = require('underscore');
exports.filter = function(data) {
if(_.isArray(data)) {
console.log("Data is: " + data);
data = data[0];
}
return data;
}
my-module-test.js:
jest.dontMock('../my-module');
var testdata = [{label: "test"}, {id: 5}];
describe('test my module', function(){
it('changes some data' , function(){
var transformedData = require('../my-module').filter(testdata);
expect(transformedData).toBe(testdata[0]);
});
});
Why is jest swallowing my console.log output in "my-module.js"?
Jest seems to mock everything by default. Which is the issue I encountered.
Underscore "_" was mocked and there was no visible error, so the execution never got to the console.log in the first place.
There are three possibilities (as far as I know) to prevent such a situation:
In your package.json file, prevent mocking of node modules (which includes underscore)
"jest": {
"unmockedModulePathPatterns": ["/node_modules/"]
}
Explicitly prevent auto mocking in your test file using:
jest.autoMockOff();
Explicitly exclude underscore from mocking:
jest.unmock('underscore');

How to stub process.env in node.js?

I want to stub process.env.FOO with bar.
var sinon = require('sinon');
var stub = sinon.stub(process.env, 'FOO', 'bar');
I'm confused.
I read document, but still I don't understand yet.sinonjs docs
sinonjs is one example, not sinonjs is okay.
From my understanding of process.env, you can simply treat it like any other variable when setting its properties. Keep in mind, though, that every value in process.env must be a string. So, if you need a particular value in your test:
it('does something interesting', () => {
process.env.NODE_ENV = 'test';
// ...
});
To avoid leaking state into other tests, be sure to reset the variable to its original value or delete it altogether:
afterEach(() => {
delete process.env.NODE_ENV;
});
I was able to get process.env to be stubed properly in my unit tests by cloning it and in a teardown method restoring it.
Example using Mocha
const env = Object.assign({}, process.env);
after(() => {
process.env = env;
});
...
it('my test', ()=> {
process.env.NODE_ENV = 'blah'
})
Keep in mind this will only work if the process.env is only being read in the function you are testing. For example if the code that you are testing reads the variable and uses it in a closure it will not work. You probably invalidate the cached require to test that properly.
For example the following won't have the env stubbed:
const nodeEnv = process.env.NODE_ENV;
const fnToTest = () => {
nodeEnv ...
}
With sinon you can stub any variable like this.
const myObj = {
example: 'oldValue',
};
sinon.stub(myObj, 'example').value('newValue');
myObj.example; // 'newValue'
This example is form sinon documentation. https://sinonjs.org/releases/v6.1.5/stubs/
With that knowledge, you can stub any environment variable.
In your case it would look like this:
let stub = sinon.stub(process.env, 'FOO').value('bar');
You can use this if you want to stub a key which not present in process.env
const sinon = require('sinon')
let sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({ 'SOME_KEY': 'SOME_VALUE' });
How to quickly mock process.env during unit testing.
https://glebbahmutov.com/blog/mocking-process-env/
const sinon = require('sinon')
let sandbox = sinon.createSandbox()
beforeEach(() => {
sandbox.stub(process.env, 'USER').value('test-user')
})
it('has expected user', () => {
assert(process.env.USER === 'test-user', 'wrong user')
})
afterEach(() => {
sandbox.restore()
})
But what about properties that might not exist in process.env before the test? You can use the following package and then you will be able to test the not exist env variables.
https://github.com/bahmutov/mocked-env
In a spec-helper.coffee or something similar where you set up your sinon sandbox, keep track of the original process.env and restore it after each test, so you don't leak between tests and don't have to remember to reset every time.
_ = require 'lodash'
sinon = require 'sinon'
beforeEach ->
#originalProcessEnv = _.cloneDeep process.env
afterEach ->
process.env = _.cloneDeep #originalProcessEnv
In your test, use process.env as normal.
it 'does something based on an env var', ->
process.env.FOO = 'bar'
Like many others have pointed out the sinon way works unless you have the environment variables set before the test begins, e.g. in the file you are testing like so:
const foo = process.env.YOUR_VAR;
Gleb Bahmutov wrote an npm package that gives a nice way of setting environment variables in your tests no matter how you are referencing the environment variables.
Link to his page: https://glebbahmutov.com/blog/mocking-process-env/
Link to npm package: https://github.com/bahmutov/mocked-env
It worked great and was super easy to implement.

NodeJS - Requiring module returns empty array

Writing the simplest module we could, we write into hello.js:
var hello = function(){
console.log('hello');
};
exports = hello; \\ Doesn't work on Amazon EC2 Ubuntu Instance nor Windows Powershell
I run Node and require the module
var hello = require('./hello');
hello;
and an empty array {} gets returned when I'm supposed to get [Function].
I tried replacing exports with module.exports, but this doesn't work on my Windows Powershell. It does work on my Amazon EC2 Ubuntu Instance, so why doesn't exports work? Has the API changed? And what could possibly be happening with Powershell that neither of these work?
I know Windows isn't the most desirable development environment, but I can't get my head around such a simple mishap.
EDIT
exporting with ES6 is a little nicer
export const hello = function(){
console.log('hello');
};
importing will look like
import {hello} from './file';
Original answer
You'll want to use module.exports
var hello = function(){
console.log('hello');
};
module.exports = hello;
If just exporting one thing, I'll usually do it all in one line
var hello = module.exports = function() {
console.log('hello');
};
Extras
If you use a named function, in the event an error occurs in your code, your stack trace will look a lot nicer. Here's the way I would write it
// use a named function ↓
var hello = module.exports = function hello() {
console.log("hello");
};
Now instead of showing anonymous for the function name in the stack trace, it will show you hello. This makes finding bugs so much easier.
I use this pattern everywhere so that I can debug code easily. Here's another example
// event listeners ↓
mystream.on("end", function onEnd() {
console.log("mystream ended");
};
// callbacks ↓
Pokemon.where({name: "Metapod"}, function pokemonWhere(err, result) {
// do stuff
});
If you want to export multiple things, you can use exports directly, but you must provide a key
// lib/foobar.js
exports.foo = function foo() {
console.log("hello foo!");
};
exports.bar = function bar() {
console.log("hello bar!");
};
Now when you use that file
var foobar = require("./lib/foobar");
foobar.foo(); // hello foo!
foobar.bar(); // hello bar!
As a final bonus, I'll show you how you can rewrite that foobar.js by exporting a single object but still getting the same behavior
// lib/foobar.js
module.exports = {
foo: function foo() {
console.log("hello foo!");
},
bar: function bar() {
console.log("hello bar!");
}
};
// works the same as before!
This allows you to write modules in whichever way is best suited for that particular module. Yay!
The reason exports is not working is because of the reference conflict. The top variable in each file is module which has a property module.exports. When the module is loaded new variable is created in the background. Something like this happens:
var exports = module.exports;
Obviously exports is a reference to module.exports, but doing
exports = function(){};
forces exports variable to point at function object - it does not change module.exports. It's like doing:
var TEST = { foo: 1 };
var foo = TEST.foo;
foo = "bar";
console.log(TEST.foo);
// 1
Common practice is to do:
module.exports = exports = function() { ... };
I have no idea why it doesn't work under Windows Powershell. To be honest I'm not even sure what that is. :) Can't you just use native command prompt?

Resources