nodejs module unit test method gets called in private class - node.js

I'm trying to unit test that a method gets called in my module. The class and method are private and not exposed using module.exports. The modules I'm using for the tests are: mocha, rewire, assert, sinon.spy. The call I want to test is my error method, this currently throws an error, but might change later - so I don't want to test that an error is thrown, just test that class.error() gets called. Not sure how to procede and have tried numerous tuts online.
The class is (currently accessed in tests using rewire):
var MyClass = function MyClass(o){
var self = this
if(!o || typeof o !== 'object')
self.error('No configuration passed to MyClass')
}
MyClass.prototype.error = function(msg){
throw Error(msg)
}
My test currently, which is not working:
it('Constructs MyClass', function(done){
//check constructs normally (this passes and works)
var actual = obj.__get__("MyClass.config")
assert.deepEqual(actual, config)
/**
* check calls error method
*/
//stub class.error ?
//construct class without config
//check if class.error is called
done()
})
In pseudo code, what I'm hoping to do is:
var stub = stub(MyClass)
->do('construct', null) //no config passed
->didCall('error') //check error method is called
This may be a duplicate of: Mocking modules in Node.js for unit testing
But it is throwing an error me: Object #<Object> has no method 'expect'

To solve it I imported the private class constructor using rewire, then I overrode the error method, setting it as a sinon.spy. I construct the class, then check if the spy was called:
//check calls error method if no config passed
var obj = rewire('./path/to/my/module')
, MyClass = obj.__get__("MyClass")
, spy = sinon.spy()
MyClass.prototype.error = spy
var foo = new MyClass()
assert.equal(spy.called, true)
done()

Related

Using aws-sdk.js with require.js in a browser

I'm trying to figure out how to use the browser-based aws-sdk.js with require.js when building a web app.
If I try to build a class and include aws-sdk in the require.js define, once I try to create an instance and reference the AWS class, it says it is undefined.
For example, I have a function that checks to see if the credentials are an instance of AWS.Credentials and if not, it tries to initialize it with an STS token it retrieves via Rest.
The problem is, the code dies on the if(this.credentials instanceof AWS.Credentials) saying AWS is undefined. It even dies on my simple check of the needsRefresh.
This is what my define looks like - I'll include the 'needsRefresh' wrapper for an example of the sort of thing that is throwing the Undefined error:
define(['require','aws-sdk','jquery'],
function (require, AWS, $) {
// Aws helper class
function AwsHelper() { };
AwsHelper.prototype = {
credentials: null,
tokenNeedsRefresh: function () {
//////////////////////////////////////////////////////////////////
// errors out on the following line with: //
// TypeError: Cannot read property 'Credentials' of undefined //
//////////////////////////////////////////////////////////////////
if(this.credentials instanceof AWS.Credentials) {
return this.credentials.needsRefresh();
} else return true;
}
};
return AwsHelper;
}
);
I also tried the following format at the top of the file:
define(function (require) {
var AWS = require("aws-sdk"),
$ = require("jquery");
/* .. */
If I remove all onLoad references to the refresh code running, it will load and I can create an instance. But as soon as I call any function that references the AWS class, it dies.
How do I make sure that AWS class definition is still in global space once an instance is spawned?
Dunno what difference the paths will make (it's finding and loading the code just fine - I can see AWS Class in namespace in the debugger as it's loading but it's not in the namespace on the function call), but added on request:
requirejs.config({
baseUrl: '/js',
paths: {
lib: 'lib',
ImageUploader: 'ImageUploader'
}
});
I decided to try to play with this again and seemed to have figured out how I was going about it incorrectly. I have yet to integrate it back into my existing code setup, but I think I have a working solution.
The thing I was doing wrong was I was trying to set up the AWS class so I could load it as a module (thus why I tried wrapping it as the require.js suggested).
Playing with it this time around, I noticed something I hadn't before. I had an AWS that was undefined in the local scope and an other AWS that held the class definition in the global scope. So it was trying to specify AWS in my define that was creating the local 'null' version:
define(
["jquery","aws-sdk","dropzone","app/MyUtilities"],
function ($, AWS, Dropzone, MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
jquery and dropzone have whatever is needed to make sure they load that way, but aws-sdk seems to do some of it's own asynchronous loading. Thus the scope variable in the function is undefined.
Technically, it seems the only thing I needed defined as a module in the function wrapper is my own utilities module. So by switching it to:
define(
["app/MyUtilities","jquery","aws-sdk","dropzone"],
function (MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
... it seems $ and jquery are defined as needed, Dropzone is defined as needed and AWS is defined as needed. (and I don't get my errors when accessing the static AWS.config or AWS.Credentials definitions as I did before)

Jasmine spy on exported function on NodeJS

I've had trouble spying on exported function in a NodeJS (v9.6.1) app using Jasmine.
The app is written in typescript, transpiled with tsc in a dist folder to be executed as javascript.
App
I have a Foo utils file (foo.utils.ts) which exports functions:
import {readFile} from "fs";
export function getData(filePath: string){
const data = readFile(filePath)
// various checks happens here.
return data;
}
And a Bar class in a bar.ts file:
import {getData} from "../utils/foo.utils
export class Bar {
public do(){
// ... does stuff
const data = getData(filePath);
// etc.
}
}
Test
Now I'm trying to spyOn the exported getData method to check if it've been called and to return a mocked value. I don't want to read file in my unit test (and even to use the real getData method at all)
The bar.spec.ts file for testing:
describe("Bar", ()=>{
let bar: Bar;
beforeEach(function() {
bar = new Bar();
})
it(" should do stuff", ()=>{
const spy = spyOn(???, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
})
});
Problems
As it is a NodeJS app, I've been trying to use global as the object to spy on, but I get the error:
Error: : getAttachementData() method does not exist
I've also tried to add
import * as fooutils from ".../src/utils/foo.utils
And spy on fooutils but I still goes through the exported function (and crashes when it tries to read the file...)
Now I'm kinda lost. From what I've found it's not really possible to mock an exported function (even though they should be added to the global object).
I thought about refactoring the utils file to create an utils class which exports static method and spying on them.
Questions
Am I doing something wrong ?
Is it possible to spy (and replace) exported function ?
Would using static method in class (rather than export function) work ?
Any other way to that ?
You can wrap getData in an object { getData } and write your unit test as follows.
import { getData } from "../utils/foo.utils";
...
it(" should do stuff", () => {
const spy = spyOn({ getData }, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
});

Mocking constructor functions in node

How do other node developers who use sinon mock out constructor calls within their unit tests? For example, suppose I have some function foo
function foo() {
var dependency = new Dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
and in a separate test file I have some test in which I want to verify what the Dependency constructor is called with (args), and I need to control what it returns
it('should call Dependency constructor with bar', function() {
var foo = require('myModule').foo
var DependencyMock; //code to make the mock
foo();
expect(DependencyMock.calledWith(bar)).to.equal(true);
});
The problem is that sinon can only mock functions attached to an object, so we have to attach the constructor to an object before it can be mocked.
What I've been doing is just making an object to attach the constructor to in the module making the constructor call, calling the constructor as a method of that object, then exporting the object to use it in tests:
var Dependency = require('path/to/dependency');
var namespace = {
Dependency: Dependency
}
function foo() {
var dependency = new namespace.Dependency(args);
// do stuff with dependency
}
exports.moduole.foo = foo;
exports.module.namespace = namespace;
testfile:
it('should call Dependency constructor with bar', function() {
var foo = require('myModule').foo;
var namespace = require('myModule').namespace;
var DependencyMock = sinon.mock(namespace, 'Dependency').returns(0);
foo();
expect(DependencyMock.calledWith(bar)).to.equal(true);
});
This works, but it feels really clunky to expose an object on my module just for the sake of testing it.
Any tips?
I think it's worth asking why you'd want to mock a constructor of a dependency instead of injecting that dependency?
Consider your example code:
// in "foo.js"
function foo() {
var dependency = new Dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
If Dependency is required for foo to work you can inject it as an argument of foo:
// in "foo.js"
function foo(dependency) {
// do stuff with dependency
}
exports.module.foo = foo;
// in "bar.js"
var foo = require('./foo.js')(new Dependency(args));
With this change it's now trivial to inject any Test Double in your tests (to find out more about JavaScript Test Doubles have a look at my article on the subject).
This approach makes the dependencies of your function/module explicit, but requires you to wire them up at some point (here: require('./foo.js')(new Dependency(args));).
If you didn't want to wire things up manually there's another approach you can take using rewire and replacing constructor with factory method:
// in "dependency.js"
module.exports= function(args) {
return new Dependency(args);
}
// in "foo.js"
var dependency = require('./dependency');
function foo() {
var dep = dependency(args);
// do stuff with dependency
}
exports.module.foo = foo;
and in your test:
var rewire = require("rewire"),
foo = rewire("../lib/foo.js");
it('should call dependency... ', function() {
foo.__set__("dependency", /* some spy */ );
foo();
});
Hope this helps!
Jan
I used a workaround for this:
// Keep a reference to your dependancy class.
const dependencyClass = Dependency;
let item = new Dependency();
// Replace class with a stub.(optionally return an item.
Dependency = sinon.stub(Dependency, 'constructor').returns(item);
// Call your code that you expect the class constructor to be called.
foo();
assert.isTrue(Dependency.calledWithNew());
assert.equal(1, Dependency.callCount);
// Restore class reference.
Dependency = dependencyClass;
Additionally, in the above case, an item is returned, so the user can have access to the dependency for further testing.
eg.
assert.equal(item.someValue, 10);
You can have other problems using this, eg defined properties will no longer be available for the class.
I agree with Jan Molek, use this in case you cannot change the code.
Not tried, but this could work: stub the constructor function of Dependency and let it return a mock.
var constructor = sinon.stub(Dependency.prototype, "constructor");
constructor.returns(sinon.mock(Dependency));
I encountered a similar problem with modules that expose functions rather than objects - this was my solution. Just putting this here as another way to go about tackling the issue but I must say solving the problem via better design, as Jan Molak suggested, seems like a better approach.

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.

Jasmine spies only detect calls on methods called on Node.js exports object

I'm trying to spy on dependencies in a Node.js module from within a Jasmine spec, and I'm encountering some weird behaviour.
Jasmine spies on a method that resides in an object exposed by the exports object in the module. When I call the spied-on method in the module using
exports.umap()
the spy detects it. But when I call it in the module using a variable name I assigned to the same method
var umap = exports.umap
umap()
the spy doesn't detect it.
This seems weird to me given that var umap refers to the same method as exports.umap. Anyone have any ideas why this is? Code below.
(The whole reason I'm doing this is that I want to stub my module's dependencies without the need to design the module as a class and use dependency injection.)
// mapper.js
exports.umap = require('underscore').map;
var umap = exports.umap;
function map () {
return exports.umap([1,2,3], function(el) {console.log(el);return el * 2});
// umap() goes unnoticed by the spy
// return umap([1,2,3], function(el) {console.log(el);return el * 2});
}
exports.map = map;
.
// mapper_spec.js
var mapper = require ('../../lib/mapper');
var map = mapper.map;
describe('mapper', function() {
describe('map', function() {
it('calls umap', function() {
var mapped;
spyOn(mapper, 'umap').and.callThrough();
mapped = mapper.map();
expect(mapper.umap).toHaveBeenCalled();
expect(mapped).toEqual([2,4,6]);
});
});
});
UPDATE
I'm still curious about this issue, however https://www.npmjs.org/package/proxyquire made my whole "spying-on-things-attached-to-export-object" strategy moot.

Resources