JavaScript unit testing with external module - javascript

I am working on an existing node project where the code structure for most of the js files looks something like following.
var mod1 = require("mod1");
var mod2 = require("mod2");
var modn = require("moden");
function func1(data, callback) {
// Do some validation with data etc..
// Use one or more imported modules here
}
function func2(data, callback) {
// Do some validation with data etc..
// Use one or more imported modules here
}
exports.func1 = func1
exports.func2 = func2
How can unit test func1, while not being dependent on the imported modules? How can I mock/stub them?
I come from the Java world so I am familiar with the mocking concepts, but here I am not sure how can I mock the globally declared module imports using require.
Currently we are using nodeunit for the unit testing purpose, but it tests very small part of the code.
I am reading about simon.js and testdouble but not sure how to use them to mock the global variables.
Any help/direction is appreciated.

For overriding dependencies during testing I recommend to use in your situation the https://github.com/thlorenz/proxyquire module
For quick exmple I paste example from project github
foo.js:
var path = require('path');
module.exports.extnameAllCaps = function (file) {
return path.extname(file).toUpperCase();
};
module.exports.basenameAllCaps = function (file) {
return path.basename(file).toUpperCase();
}
foo.test.js:
var proxyquire = require('proxyquire')
, assert = require('assert')
, pathStub = { };
// when no overrides are specified, path.extname behaves normally
var foo = proxyquire('./foo', { 'path': pathStub });
assert.equal(foo.extnameAllCaps('file.txt'), '.TXT');

Related

Add functions in other folder, to an object in this folder

I want to create an object that would import functions from another folder and it would look something like this:
class = {
functions: {
//All functions here
}
}
The functions would be inside of a different folder, however, I want to make some sort of importer in which it would make new classes for each new function/file it finds inside of the folder.
someFunction.js Function File:
function someFunction() {
console.log("this is some function");
}
So I would like for something to look like this:
class.functions.someFunction()
No, I do not want to have it hard coded into the object, I want to import all functions from a folder and create functions like that.
Well, first I wan't to answer your question as I think you want, even if I also think it is not the correct way to proceed.
I'll also assume that with class you are not referring to an actual ES6 Class, but we are talking about a plain object.
So this is the code:
const fs = require('fs');
const path = require('path');
function importer(dirPath) {
const absoluteDirPath = path.normalize(
path.isAbsolute(dirPath)
? dirPath
: path.resolve(process.cwd(), dirPath)
);
const output = {
functions: {}
};
const content = fs.readdirSync(path.normalize(absoluteDirPath));
content.forEach((basename) => {
const absoluteItemPath = path.join(absoluteDirPath, basename);
if (fs.statSync(absoluteItemPath).isFile() && /\.js$/i.test(basename)) {
output.functions[basename.slice(-3)] = require(path.relative(
__dirname,
absoluteItemPath
));
}
});
return output;
}
module.exports = importer;
For this to work, all your functions in your files should be exported like:
module.exports = function myFunction() {};
To use the 'importer', you just do:
const artemis = importer('/path/to/directory'); // PATH MUST BE ABSOLUTE OR RELATIVE TO CWD.
/*
SUPPOSING THAT YOUR DIRECTORY CONTAINS THE FOLLOWING FILES:
function1.js
function2.js
Then you can do:
artemis.function1();
artemis.function2();
Please note that your files must be named in a JS friendly way (a valid string for an object key).
*/
A final important note about this odd method: This will only ever work in a NodeJS environment. Even if functions could have worked in other environments (like a browser). The next method, will work for any ECMAScript environment after proper building process: transpilation (EX: Babel) and bundling (EX: Webpack).
Suggested Solution
Use ES6 Static import / export like modern JS libraries do. This comes with huge benefits, from static code analysis to tree shaking and more.
Let's suppose the following hierarchy:
// - index.js
// - internals/
// - index.js
// - module-1.js
// - module-2.js
internals/module-1.js
function module1() {}
export {module1};
internals/module-2.js
import {module1} from 'module-1.js';
function module2() {
// YOU CAN USE module1 IF YOU NEED. (AVOID CIRCULAR REFERENCES)
module1();
}
export {module2};
internals/index.js
import {module1} from './module-1.js';
import {module2} from './module-2.js';
export {module1, module2};
index.js
import * as moduleGroup from './internals/index.js';
export {moduleGroup};
Finally, where you import your moduleGroup, you can do:
moduleGroup.module1();
moduleGroup.module2();
Obviously this is a basic scenario, but this is, IMHO, the correct way to deliver a group of functions and other stuff. Please let me know if you have any doubt.

How to handle external modules when writing unit tests for server-side Javascript

I am unit testing a JS file that has a require that points to an external module. How do I handle the external module when running UT if it keeps on looking for that external module locally?
I already tried mocking a function from the script, but I do not think that is the proper way to test its components. What I really want is for my test file to point to the script file, and that script file will either skip the call for the require part that calls the external modules OR find a way to override or ignore that line so that I can start referencing the functions and components I need in the script file for unit testing.
Deal.js
var cordra = require('cordra');
var util = require('util');
exports.beforeSchemaValidation = beforeSchemaValidation;
function beforeSchemaValidation(deal, context) {
...
return deal;
}
DealTest.js
var assert = require('chai').assert;
const beforeSchemaValidation = require('../rules/Deal').beforeSchemaValidation;
describe('Deal', function() {
it('beforeSchemaValidation should return object', function() {
let result = beforeSchemaValidation();
assert.typeOf(result, Object);
});
});
I want my test file to run through ../rules/Deal without getting a thrown error of Error: Cannot find module 'cordra' and/or 'util' so I can test the rest of its components.
You need to do it in following way :
var cordra = require('cordra');
var util = require('util');
function beforeSchemaValidation(deal, context) {
...
return deal;
}
module.exports = {
beforeSchemaValidation: beforeSchemaValidation }

Separating functionality out into separate index.js files

I'm collaborating on a new NodeJS module with a colleague and the file structure is as follows:
index.js
let MyModule = require('./src/index.js);
MyModule = new MyModule();
module.exports = MyModule;
src/index.js
const depA = require('depA');
const MyModule = function MyModule() {
this.doStuff = function doStuff() {
console.log('doing stuff...)
}
}
module.exports = MyModule;
I'm wondering if there is any need/value to split into these two index.js files and whether it would be better to have just one top level file. Is this considered an anti-pattern? I did wonder if this approach works better when you have a complex library with functionality split out across many feature files.

Avoid to repeat require statements in nodejs modules

Currently I'm develloping an app in Express and I've created several services which are modules called from a single index.js file:
"use strict";
const MAX_TEXT_LENGTH = 100;
var mysql = require('mysql')
, _ = require('underscore')
, Q = require('q')
, moment = require('moment')
, env = process.env.NODE_ENV || 'development'
module.exports.module1 = require('./module1');
module.exports.module2 = require('./module2');
module.exports.moduleN = require('./moduleN');
Thanks to this structure I can now call a single time to var Services = require('./services') and I've access to all services.
The problem is that the modules, constants and "global" vars used in this file aren't available in the modules so I have to call them many times.
I've also tried to name vars as let but this doesn't work.
Is there any way to be able to define modules and variables in this index.js file and use them inside modules?
Thanks!!!
In your modules, instead of returning only one simple value, return an object where each key is one the variables that you want to export. Something like this.
var myModuule = {
varIWantToExport: 1,
methodIWantToExport: function() { return "Hello world!";}
}
module.exports = myModule;
And then, when you require it in other modules, you can do something like this:
var myModule = require("./my-module");
var myMethod = myModule.methodIWantToExport
var myVar = myModule.varIWantToExport;
Obviously this only applies to your modules. If you are trying to use some variables/methods that are not publicly exposed in a module, maybe you're doing something wrong.

Use ES6 library in Ember-cli project

I have a small EmberJS application that uses Ember-Cli. My application has a private ES6 library that is a bower dependency. Basically, what I want is to import the library and use it wherever I want.
If I'm not wrong, I should transpile the library in my brocfile.js and use it afterwards. Unfortunately, I cannot provide too much concrete information but I'll try my best to be the clearer possible.
My external library is named main-lib and is structured the following way (it is working in another project):
bower_components
main-lib
api.js
main.js
message.js
In the main.js file, I have the following:
import Api from 'main/api';
import Message from 'main/message';
var main = {};
main.api = Api;
main.message = Message;
export default main;
So, what I want to do, is, in my application, to import main and use the different functions it contains.
Example, in a certain emberjs controller:
import Main from 'main';
//use Main here
To do so, I thought of doing the following in my brocfile.js
var tree = 'bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'mainLib'
}
});
However, this does nothing. Basically, I want the transpiled files to be included in vendor.js or somewhere where I would be able to use the library by importing it.
There's something I'm missing here but I can't pinpoint it.
Edit1: After adding these lines at the end of my brocfile.js:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
I can get an ES5 that looks like this:
define(['main/api', 'main/message'], function(api, message) {
var main = {};
main.api = Api;
main.message = Message;
var _main = main;
return _main;
});
The problem is that it does not import main/api and main/message as well. Do I have to repeat the code for each file that I want ?
Also, the file is not concatenated in vendor.js but simply but at the root of /dist
You have the following: import Api from 'main/api' - but I don't see a folder called main in what you've explained - only a folder called main-lib ...
Could it be that main/api and main/message are not included because they actually don't exist? You might need to use main-lib/api and main-lib/message in your main.js file
The integration of Broccoli with ember-cli already includes a transpiler, so I think something like this should be enough:
app.import('bower_components/main-lib/main.js', {
type: 'vendor',
exports: { 'main': ['default'] }
);
And then you can:
import Main from 'main';
With what you currently have in your Brocfile you still need to merge your amdFiles (app.import would do that for you).
Something like:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
None of this is tested, but you get the idea.
Ember seem to be advocating using: https://www.npmjs.com/package/broccoli-es6modules
This would mean importing your module would look something like:
var mergeTrees = require('broccoli-merge-trees');
var tree = './bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'main-lib'
}
});
module.exports = mergeTrees([app.toTree(), amdFiles])

Categories

Resources