I have the following JavaScript:
var async = require('async');
var MyOutputModel = function(persistenceModel, callback) {
async.each(persistenceModel.helpOffers, function(helpOffer, asyncCallback) {
console.log('Original source (loop)');
//Do something ...
asyncCallback();
}, function(err) {
console.log('Original source (done)');
console.log(err);
if(err) return callback(err);
return callback(null, _this);
});
};
module.exports = MyOutputModel;
I would like to test the path containing if(err) return callback(err);...
For doing so I see the only possibility in dependency injection with rewire (https://www.npmjs.com/package/rewire).
In my unit test:
var rewire = require('rewire');
var MyOutputModel = rewire('...');
var AsyncMock = function() {};
AsyncMock.prototype.each = function(array, successCallback, errorCallback) {
console.log('Inside mock!');
callback(new Error());
};
var asyncMock = new AsyncMock();
MyOutputModel.__set__('async', asyncMock); //Rewire for require-dependency-injection
//Act
new CdObjectOutputModel(cdObjectPersistenceModel, function(err, cdObjectOutputModel) {
//Assert
assert(err);
});
However the "mock" doesn't seem to be used. In my unit test I never see 'Inside mock!' but 'Original source (loop)' and 'Original source (done)' on the console. And there is no error.
Anything I do wrong? I've already used rewire for a similar use case and it worked perfectly. Or is there another approach?
This is what rewire does under the hood:
For each rewired path it'll actually read the contents of the file pointed by path which we will call the body
it'll also generate two strings, the first one is called the prelude and it has some helpful vars to avoid overwriting the global ones with __set__, e.g. if we wanted to override the global console as if it were local on our module the contents of the prelude var would be: var console = global.console, this awesome detection of globals which can be overridden locally is done here
The second one is called the appendix and this string contains actually the definitions of the __set__, __get__ and there's also __with__ source, each one is a function defined over module.exports but remember that we're still dealing with strings
Imagine that there's a way to create a fake/test module out of strings! That's what wired does, it concatenates the prelude, the body and the appendix in one big string and it then creates a module with require('module')
e.g. (remember that this is a big string)
// prelude start
var console = global.console
// a lot of globals that can be overriden are defined here :)
// prelude end
// body start
// I'll paste the contents of the rewire's README
var fs = require("fs");
var path = "/somewhere/on/the/disk";
function readSomethingFromFileSystem(cb) {
console.log("Reading from file system ...");
fs.readFile(path, "utf8", cb);
}
exports.readSomethingFromFileSystem = readSomethingFromFileSystem;
// body end
// appendix start
// generation of three functions to set the variables defined above
Object.defineProperty(module.exports, '__set__', {
enumerable: false,
value: function () {
// set contents, we will see this later
}
});
Object.defineProperty(module.exports, '__get__', { ... });
Object.defineProperty(module.exports, '__with__', { ... });
// appendix end
So what rewire does is kind of create a module with this string and return it, and voila you have a magic module with three additional properties, the next step is actually find out what __set__ does, the __set__ source boils down to the following operation:
function (key, value) {
eval(key + ' = ' + value);
}
And that's it so now we know that executing __set__ will just eval whatever key (which for us is any local var) you want, to be set as value but ONLY in this test/fake module.
Now going back to your problem I see that this might be the source of it:
new CdObjectOutputModel(...);
I'm assuming that CdObjectOutputModel is defined perhaps as a subclass of MyOutputModel but remember that in your test var MyOutputModel = rewire('...'); is actually a test/fake module so you're not really overriding the original code of MyOutputModel but overriding kind of a copy of it which exists only locally, I guess that you have to rewire CdObjectOutputModel or perhaps locally make CdObjectOutputModel inherit from the rewired version of MyOutputModel
Just replace callback with errorCallback and it should work fine.
Related
I have a work flow, something like this:
gulp.src('**/*.js')
.pipe(doSomething())
.pipe(dest('out'));
I want doSomething to return a stream pipeline, say something like uglify().pipe(header(...)).pipe(rename(...)) so that the effective result of this is
gulp.src('**/*.js')
.pipe(uglify())
.pipe(header(...))
.pipe(rename(...))
.pipe(dest('out'));
Obviously doSomething can't return uglify (because dest would read from that), and it can't return rename (because src would write to that). So my plan was to build a Duplex stream that acted like a wrapper around this pipeline fragment, resulting in this:
first = uglify();
last = first
.pipe(header(...))
.pipe(rename(...));
gulp.src('**/*.js')
.pipe(wrapper(first, last) /* writes go to first, reads go to last */)
.pipe(dest('out'));
I've taken a stab at writing a wrapper, but I don't have enough Node Stream experience to know that I'm going down the right path at all, and why it's not working. Here's what I've got so far:
var encapsulateStream = function(first, last, logger) {
logger('encapsulate');
let resultStream = new Duplex({
readableObjectMode: true,
writableObjectMode: true,
read(size) {
logger('read');
while (true) {
let chunk = last.read(size);
if (logger) logger('[in]', '--->', chunk);
if (!this.push(chunk) || chunk === null) {
return;
}
}
},
write(chunk, enc, next) {
logger('write');
if (logger) logger('--->', '[first]', chunk);
first.write(chunk, enc, next);
}});
resultStream.on('finish', function() {
logger('finish');
first.end();
forwardStream(last, resultStream, logger);
});
return resultStream;
};
I get "encapsulate" in my logs, but nothing else. Where am I going wrong, and is this the right approach?
There's no need to implement something like this yourself. The lazypipe library is widely used and does pretty much exactly what you want.
Here's a quick example that illustrates its usage for your use case:
var lazypipe = require('lazypipe');
var doSomething = lazypipe()
.pipe(uglify)
.pipe(header, ...)
.pipe(rename, ...);
gulp.src('**/*.js')
.pipe(doSomething())
.pipe(gulp.dest('out'));
I have a module where I load a mustache template file. I would like to write a unit test for this. I am trying to use mocha, chai and rewire.
Here is my module.js:
var winston = require('winston');
var fs = require('fs');
var config = require('./config.js');
exports.logger = new winston.Logger({
transports: [
new winston.transports.File(config.logger_config.file_transport),
new winston.transports.Console(config.logger_config.console_transport)
],
exitOnError: false
});
exports.readTemplateFile = function(templateFile, callback) {
fs.readFile(config.base_directory + templateFile + '.tpl.xml', 'utf8', function (err, data) {
if (err) {
logger.error('Could not read template ' + templateFile + ': ' + err);
}
callback(data);
});
};
In the callback function I use mustache to do something with the template.
What is the best way to test this?
Maybe I will have to rewire the fs.readFile? As the file won't be there when the test will be executed. The Winston logger is also an interesting part I guess, not sure if it will be initialized if I import this within a mocha test. My first test shows the logger is undefined.
One of the most important unit testing principle is testing very small piece of code. To achieve that you should definitely mock or stub calls to functions that not belong to testing code (readFile and logger.error in this case). For provided code you can make three test cases:
calling readFile with proper arguments
calling error if err is present
calling callback function with proper argument
Your callback function should be tested outside this code, for e.g. by providing fake data as parameter:
define('Some test', () => {
it('should return true', () => {
expect(callbackFunction('fakeData').to.be.ok);
});
});
I am writing test cases for NODE JS API. But wherever console.log() is there in routes or services of NODE JS File, it gets printed to CLI. Is there a way to mock these so that these won't get printed in CLI.
I have explored couple of libraries like Sinon, Stub for mocking. But couldn't grasp the working of those libraries.
You can override function entirely: console.log = function () {}.
You should not try to mock console.log itself, a better approach is for your node modules to take a logging object. This allows you to provide an alternative (ie. a mock) during testing. For example:
<my_logger.js>
module.exports = {
err: function(message) {
console.log(message);
}
}
<my_module.js>
var DefaultLogger = require('my_logger.js');
module.exports = function(logger) {
this.log = logger || DefaultLogger;
// Other setup goes here
};
module.exports.prototype.myMethod = function() {
this.log.err('Error message.');
};
<my_module_test.js>
var MyModule = require('my_module.js');
describe('Test Example', function() {
var log_mock = { err: function(msg) {} };
it('Should not output anything.', function() {
var obj = new MyModule(log_mock);
obj.myMethod();
});
});
The code here I've simplified, as the actual test isn't the reason for the example. Merely the insertion of alternative logging.
If you have a large codebase with lots of console.log calls, it is better to simply update the code as you add tests for each method. Making your logging pluggable in this way makes your code easier and more receptive to testing. Also, there are many logging frameworks available for node. console.log is fine during development when you just want to dump out something to see what's going on. But, if possible, try to avoid using it as your logging solution.
I could not find a solution which only hides the console.log calls in the module to be tested, and mocks none of the calls of the testing framework (mocha/chai in my case).
I came up with using a copy of console in the app code:
/* console.js */
module.exports = console;
/* app.js */
const console = require('./console');
console.log("I'm hidden in the tests");
/* app.spec.js */
const mockery = require('mockery');
var app;
before(() => {
// Mock console
var consoleMock = {
log: () => {}
}
mockery.registerMock('./console', consoleMock);
// Require test module after mocking
app = require('./app');
});
after(() => {
mockery.deregisterAll();
mockery.disable();
});
it('works', () => {});
You could do something along the lines of adding these before/after blocks to your tests, but the issue is that mocha actually uses console.log to print the pretty messages about the results of the test, so you would lose those
describe('Test Name', function() {
var originalLog;
beforeEach(function() {
originalLog = console.log;
console.log = function () {};
});
// test code here
afterEach(function() {
console.log = originalLog;
})
})
The problem is that your output would just look like
Test Name
X passing (Yms)
Without any intermediate text
I have uploaded a pdf via a MEAN stack web application using fs. I want to extract certain fields from the pdf and display them on the web app. I have looked at a couple npm packages like pdf.js, pdf2json. I can't figure out the documentation and javascript callbacks used in the examples available. Please help!
I hope I can help answer your question. Using pdf2json can be used to parse a pdf and extract the text. There are a couple of steps that need to be taken to get it working. I have adapted the example from https://github.com/modesty/pdf2json.
The setup is to install pdf2json in the node app, and also underscore. The example page didn't explain the need to define your own callback functions. It also used self instead of this to register them. So, with the appropriate changes the code to extract all the text from the pdf will be something like this:
// Get the dependencies that have already been installed
// to ./node_modules with `npm install <dep>`in the root director
// of your app
var _ = require('underscore'),
PDFParser = require('pdf2json');
var pdfParser = new PDFParser();
// Create a function to handle the pdf once it has been parsed.
// In this case we cycle through all the pages and extraxt
// All the text blocks and print them to console.
// If you do `console.log(JSON.stringify(pdf))` you will
// see how the parsed pdf is composed. Drill down into it
// to find the data you are looking for.
var _onPDFBinDataReady = function (pdf) {
console.log('Loaded pdf:\n');
for (var i in pdf.data.Pages) {
var page = pdf.data.Pages[i];
for (var j in page.Texts) {
var text = page.Texts[j];
console.log(text.R[0].T);
}
}
};
// Create an error handling function
var _onPDFBinDataError = function (error) {
console.log(error);
};
// Use underscore to bind the data ready function to the pdfParser
// so that when the data ready event is emitted your function will
// be called. As opposed to the example, I have used `this` instead
// of `self` since self had no meaning in this context
pdfParser.on('pdfParser_dataReady', _.bind(_onPDFBinDataReady, this));
// Register error handling function
pdfParser.on('pdfParser_dataError', _.bind(_onPDFBinDataError, this));
// Construct the file path of the pdf
var pdfFilePath = 'test3.pdf';
// Load the pdf. When it is loaded your data ready function will be called.
pdfParser.loadPDF(pdfFilePath);
I am running the code out of my server side controller.
module.exports = (function() {
return {
add: function(req, res) {
var tmp_path = req.files.pdf.path;
var target_path = './uploads/' + req.files.pdf.name;
fs.rename(tmp_path, target_path, function(err) {
if (err) throw err;
// delete the temporary file, so that the explicitly set temporary upload dir does not get filled with unwanted files
fs.unlink(tmp_path, function() {
if (err) throw err;
//edit here pdf parser
res.redirect('#/');
});
})
},
show: function(req, res) {
var pdfParser = new PDFParser();
var _onPDFBinDataReady = function (pdf) {
console.log('Loaded pdf:\n');
for (var i in pdf.data.Pages) {
var page = pdf.data.Pages[i];
// console.log(page.Texts);
for (var j in page.Texts) {
var text = page.Texts[j];
// console.log(text.R[0].T);
}
}
console.log(JSON.stringify(pdf));
};
// Create an error handling function
var _onPDFBinDataError = function (error) {
console.log(error);
};
pdfParser.on('pdfParser_dataReady', _.bind(_onPDFBinDataReady, this));
// Register error handling function
pdfParser.on('pdfParser_dataError', _.bind(_onPDFBinDataError, this));
// Construct the file path of the pdf
var pdfFilePath = './uploads/Invoice_template.pdf';
// Load the pdf. When it is loaded your data ready function will be called.
pdfParser.loadPDF(pdfFilePath);
},
//end controller
}
I just have a quick question there:
I am using Node.JS to write a commandline tool that validates JSON Files with JSON Schemas. So, now I have a problem that when wanting to get all the schemas, that I always get "undefined" for using a async function but otherwise only sync functions.
For this commandline tool async is NOT needed.
Could someone help me out and give me a hand on how to make it work just fine?
var getJSONSchemaFiles = function (dir) {
results2 = [];
var recursive = require('recursive-readdir');
recursive(dir, function (err, files) {
// Files is an array of filename
// console.log(files);
files.forEach(function (entry) {
if (entry.indexOf(".schema.json") > -1) {
results2.push(entry);
}
});
console.log(results2);
});
return results2;
};
I am using the npm "recursive-readdir" but I think that I do not even need a npm for this kind of thing?
Ok, this enumerates all files under the given path synchronously:
var fs = require('fs');
function recursiveReaddir(path) {
var stat = fs.lstatSync(path);
if(stat.isFile())
return [path];
if(!stat.isDirectory())
return [];
return [].concat.apply([], fs.readdirSync(path).map(function(fname) {
return recursiveReaddir(path + '/' + fname);
}));
}
Use glob module https://github.com/isaacs/node-glob. There is async and Sync methods like: glob.sync(pattern, [options]); and glob(pattern, [options], cb);
Example from their docs:
var glob = require("glob")
// options is optional
glob("**/*.js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
})