Unit Testing - Mock Methods within Methods? - javascript

I am writing unit tests for a Node.js application, and I am wondering if I am mocking the correct parts of the code.
The example below is a hypothetical class that has two static methods.
The method isTokenValid calls another method, decodeToken which takes the token and a callback. The callback is defined inside of isTokenValid. Both these methods belong to the same class.
When unit testing isTokenValid my approach is to mock the decodeToken method.
It is clear to me that when unit testing, dependencies such AJAX requests should be mocked. However, does that also hold true for this type of dependency or am I being too granular?
Is mocking decodeToken the right approach to unit testing isTokenValid?
var TokenClass = {};
TokenClass.isTokenValid(token) {
TokenClass.decodeToken(token, function(err, decoded) {
if (err) {
console.log('There was a validation error');
}
if (decoded) {
return true
};
}
}
TokenClass.decodeToken(token, callback) {
// some logic here to decode token
if (err) {
return callback(err);
}
// if token is not valid
if (!validToken) {
return callback(null, undefined);
}
// if token is valid
return callback(null, decoded);
}
}

There are two approaches.
In classic unit tests you can mock everything that is external to your tested unit - in this case isTokenValid method is your unit. But that approach isn't practical.
The best way is to mock things that doesn't let your tests run in isolation and in deterministic way (same result every time).
If decodeToken is not calling any external resource (url, database, file system) then you don't have to mock that out. However if it does call external resource, then decodeToken should be implemented in another object, i.e. TokenDecoder and injected into TokenValidator, then for unit test of TokenValidator you can inject mocked TokenDecoder that is not calling any external resource.
TokenDecoder then should be tested using integration test, but that is another topic.

Related

Passing context implicitly across functions and javascript files in nodes.js

I have created a web server i node.js using express and passport. It authenticates using an oauth 2.0 strategy (https://www.npmjs.com/package/passport-canvas). When authenticated, I want to make a call such as:
app.get("/api/courses/:courseId", function(req, res) {
// pass req.user.accessToken implicitly like
// through an IIFE
createExcelToResponseStream(req.params.courseId, res).catch(err => {
console.log(err);
res.status(500).send("Ops!");
});
});
My issue is that i would like, in all subsequent calls from createExcelToResponseStream, to have access to my accessToken. I need to do a ton of api calls later in my business layer. I will call a method that looks like this:
const rq = require("request");
const request = url => {
return new Promise(resolve => {
rq.get(
url,
{
auth: {
bearer: CANVASTOKEN // should be req.user.accessToken
}
},
(error, response) => {
if (error) {
throw new Error(error);
}
resolve(response);
}
);
});
};
If i try to create a global access to the access token, i will risk
race conditions (i think) - i.e. that people get responses in the context of another persons access token.
If i pass the context as a variable i have to refactor a
lof of my code base and a lot of business layer functions have to
know about something they don't need to know about
Is there any way in javascript where i can pass the context, accross functions, modules and files, through the entire callstack (by scope, apply, bind, this...). A bit the same way you could do in a multithreaded environment where you have one user context per thread.
The only thing you could do would be
.bind(req);
But that has has to be chained into every inner function call
somefunc.call(this);
Or you use inline arrow functions only
(function (){
inner=()=>alert(this);
inner();
}).bind("Hi!")();
Alternatively, you could apply all functions onto an Object, and then create a new Instance:
var reqAuthFunctions={
example:()=>alert(this.accessToken),
accessToken:null
};
instance=Object.assign(Object.create(reqAuthFunctions),{accessToken:1234});
instance.example();
You could use a Promise to avoid Race conditions.
Let's have this module:
// ContextStorage.js
let gotContext;
let failedGettingContext;
const getContext = new Promise((resolve,reject)=>{
gotContext = resolve;
failedGettingContext = reject;
}
export {getContext,gotContext, failedGettingContext};
And this inititalization:
// init.js
import {gotContext} from './ContextStorage';
fetch(context).then(contextIGot => gotContext(contextIGot));
And this thing that needs the context:
// contextNeeded.js
import {getContext} from './ContextStorage';
getContext.then(context => {
// Do stuff with context
}
This is obviously not very usable code, since it all executes on load, but I hope it gives you a framework of how to think about this issue with portals... I mean Promises...
The thing that happens when you call the imported 'gotContext', you actually resolve the promise returned by 'getContext'. Hence no matter the order of operations, you either resolve the promise after the context has been requested setting the dependent operation into motion, or your singleton has already a resolved promise, and the dependent operation will continue synchronously.
On another note, you could easily fetch the context in the 'body' of the promise in the 'ContextStorage' singleton. However that's not very modular, now is it. A better approach would be to inject the initializing function into the singleton in order to invert control, but that would obfuscate the code a bit I feel hindering the purpose of the demonstration.

Test callback using in a route with supertest

I started to istanbul as a test coverage tool with mocha and one of the great things is that it shows you the paths (branches) you have tested in your test code logic.
There is a path that can only be taken if a error occurs on the database.
Screenshot of the part I am interested in testing
The [I] indicates the the first if was not tested.
The problem is that it uses a callback function(err, data) and the error is passed to this callback through a the mongoose model method find(), and because of that I don't have the flow control of this part of the code.
In this specific case I using supertest which is a module to test routes in node.js and it make requests to a route that calls a mongoose model method find().
What would be the best option to test this path? Create a stub to simulate the method? Or just remove the if?
EDIT: I noticed that I was using an anonymous function as a callback (err, data) and doing so I can't test it since it's not exposed to the outer scope. One approach I had in mind was to create a function:
handleDbFetchingResponse(res) {
return function(err, data) {
let response = {};
if (err) {
response = {error: true, message: 'Error fetching data'};
} else {
response = {error: false, message: data};
}
res.json(response);
}
}
Now I can expose the function and test it, I create another problem though. Since the other express routes have another logic when fetching data from the database I will have to create a handler function for each one of them. Maybe there is a way to create a handlerBuilder function that returns a new handler passing different arguments to deal with specific cases.

How do I test a function that sends a request in browser that requires authentication?

I'm in the process of writing unit tests in Mocha and Chai, and I'm trying to figure out how to test a function that that uses the fetch API to send a request that requires a session to access. This function is an ES6 method that returns a thenable object.
_auth(record) {
var authMetadata = this._getAuthMetadata(record);
var authUrl = `/${this._getPortal(window.location.pathname)}/tlist_child_auth.html?${this._encodeUri(authMetadata)}`;
if (getPortal() !== 'guardian') {
authUrl += `&frn=${this.coreTableNumber}${this.foreignKey}`;
}
return fetch(authUrl, {
credentials: 'include'
}).then(function(rawData) {
return rawData.text();
});
}
tlist_child_auth.html is the page that requires session authorization to access. Is there any easy way to do this, or should I seek to "invent the wheel"?
With unit testing, the idea is to test the unit that you're interested in, and only that. In this instance, the unit makes use of fetch, which itself goes off and talks to websites, so in effect your unit test ends up doing a round trip to a website so you end up "testing" that too. Not what you want in a unit test.
The correct way to test just the part you're interested in is to mock the fetch function, and use the mock to verify it is called in the way you expect, plus also return data from the mock to your unit that it can use to complete the test. You may perform other checks to ensure your unit has processed the return in the way you expected.
It appears that there is at least one npm package available for mocking fetch.

How can I unit test this code snippet?

I've just recently moved to a new project that deals mainly in Javascript (as a Node.js web application).
I'm a fairly TDD focused developer, and am trying to figure out the best approaches / patterns to ensure that what we end up building is unit-testable and maintainable.
I've been trying to wrap the following code snippet with unit tests, but am having trouble getting good code coverage over the anonymous function passed in as the request callback.
I have mocked the request object using the rewire.js library, and can successfully test that the logger was called, that request was called with the correct parameters, but how do I complete the test coverage for this?
function _makeRequest(apiName, options, payload, callback) {
logger.info('DS API %s Request:\n %s %s\n %s', apiName, options.method, options.url, logger.look(payload));
request(options, function(error, response, body) {
var json = 'json' in options ? body : JSON.parse(body);
if ('error' in json) {
var msg = 'DS API ' + apiName + ' Error:\n ' + logger.look(json.error);
logger.info(msg);
callback(null);
} else { // no error
logger.info('DS API %s Response:\n %s', apiName, logger.look(json));
callback(json);
}
});
}
Should I be refactoring for better testability? Is there a common approach for unit testing callbacks that I'm not aware of?
Carl put me on the right direction. I had set up my parameters for the tests with a good range of input data (to ensure that all code lines would be executed in one test or another) but, in the end, was failing to actually execute the callback parameter after passing it to the rewire.js Mock.
The callback was making it in, but I needed to execute it from within the mock to ensure that the callback code would still be executed

Node best practices: Throwing async error in constructor

I am working with Node and I have a "class" that takes a directory as a parameter. It tries to create that directory and if it fails, then it throws an error:
function Config(dir) {
fs.mkdir(dir, function(err) {
if(err) throw new Error('Error', err);
}
}
My question is, is this an approved way of doing this? If I were to use a callback, then the rest of my program would have to reside in that callback, which seems odd to me.
This issue manifested itself when I tried to write a test using mocha which won't work since the exception is thrown in an async call:
it('should throw an error on a bad directory', function() {
var fn = function() {
var badConfig = new Config('/asdf');
};
assert.throws(fn, Error);
});
I've investigated domains as a way to solve the unit test issue, but that didn't seem to solve my problem (or I didn't implement them correctly).
var d = domain.create().on('error', function(err) { throw err; }
d.run(function() {
function Config(dir) {
fs.mkdir(dir, function(err) {
if(err) throw err;
}
}
});
Ultimately, I'm looking for a best practice that allows me to indicate to the application that something bad happened, and allows me to create tests for that solution.
You have three possibilities:
Using a synchronous call. As AsolBerg explained, your case suits exactly why some fs functions have their synchronous equivalent. It's ok because in your case, all your application depends on one Config instance to be loaded. but there are cases
Using a callback as constructor argument.
If constructor callback sounds really too odd for you, put your initialization code into an init() method, that takes a callback. It's a matter of personnal preference, but rather use this technic.
Last option, you can returns a Future in your init() method. There are several future libraries in NodeJS, that are an elegant alternative to callback parameter. But you can't use it in your constructor... as the constructor's return is the created object.
It sounds like in this case you might actually want to make a synchronous call (e.g. the rest of your application depends on this call being finished before proceeding). So although its normally not the way you want to think about building your node apps you could use the synchronous version mkdirSync().
http://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode
Then if the call fails you can catch the error and return it and (probably) exit the app.

Categories