Mocha / Sinon Unit test JS Class and Instance issue - javascript

I'm trying to unit test some express middleware which has dependencies on some classes I've made.
Middleware.js
const MyClass = require('../../lib/MyClass');
const myClassInstance = new MyClass();
function someMiddleware(req, res) {
myClassInstance.get().then(function(resp) {
res.render('somefile.html');
});
};
Test.js
const MyClass = require('../../lib/MyClass');
const sinon = require('sinon');
const chai = require('chai');
const expect = chai.expect;
// File we are testing
const someMiddleware = require('path/to/middleware');
MyClassMock = sinon.spy(function() {
return sinon.createStubInstance(MyClassMock);
});
describe('My Middleware', function() {
let renderSpy, classStub;
let res = {
render: function() {}
}
beforeEach(function() {
renderSpy = sinon.stub(res, 'render');
})
afterEach(function() {
renderSpy.restore();
})
it('should call render function', function() {
someMiddleware.someMiddleware(req, res);
expect(renderSpy.calledOnce);
});
});
I have tried a bunch of things however I can't quite seem to actually mock this class and mock the instance created! when it runs it will try to run the actual class with it's related dependencies inside.
Cheers folks!

Your Intent: replace/stub the dependencies of your middleware.
Your problem: this isn't possible from the outside of your module. You somehow need to "intercept" which code will be used.
There are two options, both of which I have written at length about in other places (1, 2, 3), so I'll do the shortform here:
Manual dependency injection
In your middleware module, provide a __setMyClassInstance(stubbedInstance) method that is only called from tests.
Pro: very easy, no frameworks needed
Con: test-only code that opens up your module
Link seams
This is about replacing require() calls in your code with another module loader that returns objects of your liking. In your test code:
const myMiddleware = proxyquire('../../src/middleware', { '../../lib/MyClass': myStubbedInstance })
Pro: Achieves the same as DI, but requires no code changes in the module
Con: not as clear cut what is going on, requires learning a new dependency
If you are stuck after looking at these very brief explanations, I suggest looking at my provided links for long-form explanations and links to tutorials on Link Seam libraries such as proxyquire and rewire.

Related

mockImplementation() still calling the original functionality of the method

Supposed I have a code
const SomeEmailModule = require('SomeEmailModule')
const emailModule = new SomeEmailModule()
async function sendEmail(htmlBody) {
await emailModule.send(htmlBody)
return htmlBody
}
and when I used it on test using jest
const SomeEmailModule = require('SomeEmailModule')
it('can test', async () => {
const emailModule = new SomeEmailModule()
jest.spyOn(emailModule, 'send').mockImplementation()
......some code
)
the module doesn't actually mock the method send it stills do the original functionality of the method any idea why it happened?
Okay after some testing
const mockSend = jest.fn()
SomeEmailModule.prototype.send = mockSend
this solution works however I want to know how this one works while the other one doesn't
this one also works
const mockSend = jest.spyOne(SomeEmailModule.prototype, 'send').mockImplementation()
Because the emailModule instance of the SomeEmailModule class in your test case is different from the one in the file under test.
jest.spyOn(emailModule, 'send').mockImplementation();
You just install a spy on the emailModule instance created in the test case, but the emailModule instance in the file under test is not spied.
But installing spy on SomeEmailModule.prototype.send() will work, because all instances share the same prototype.

How to monkey patch a single method when using jest.mock

I'm using Jest to test a JS project which imports a third party library. I've been able to successfully mock the third-party library by doing this at the top of my test file:
jest.mock('third-party');
But now, I need to customize the mock implementation of a single method inside the third-party library. Let give diagram how the third-party library is structured because I think that's where I'm getting tripped up:
Third-Party Library Package
Exports Constructor1
properties
tons of instance methods...
Exports Constructor2
properties...
instance methodA
instance methodB <- This is what I want to monkey patch.
I want to monkey patch this because mocking this library currently gives me this in my test:
import { Constructor2 } from 'third-party';
jest.mock('third-party');
describe('Thing', () => {
var instance
beforeEach(() => {
instance = new Thing();
instance.constuctor2instance = new Constructor2();
instance.controls = instance.constuctor2instance.methodB();
// methodB returns nothing because it's mocked. I want it to return a custom implementation.
});
test('test 1', () => {
// Fake test just for example
expect(instance.constuctor2instance).toBeInstanceOf(Constructor2); // Success
jest.spyOn(instance.controls, 'nestedFunction'); // Fails because instance.controls is undefined
});
});
So, how can I provide a custom implementation for methodB without having to define the implementation of the whole third party library or even the entire Constructor2...just one method?
**EDIT with solution from below from ** #Teneff
jest.mock('third-party');
const mockControls = {
nestedFunction: jest.fn()
};
beforeEach(() => {
Constructor2.prototype.controls.mockImplementation(() => mockControls);
});
afterEach(() => {
jest.resetAllMocks();
});
You can use Constructor2's prototype like this
const mockControls = {
nestedFunction: jest.fn(),
};
Constructor2.prototype.methodB.mockImplementation(() => mockControls);
and you won't have to spy on it, you'd be able to make assertions like so:
expect(mockControls.nestedFunction).toHaveBeenCalledWith(...);

How to spy on a method inside a dependent module using jasmine-node

I am trying to write jasmine tests for a module-(say moduleA) which 'requires' another module-(moduleB).
======> moduleB.js
function moduleBFunction(){
console.log('function inside moduleB is called');
}
======> moduleA.js
var moduleB = require('./moduleB');
function moduleAfunction(input){
if(input){
moduleB.moduleBFunction()
}
}
I want to write a jasmine test case that tests when i call moduleAfunction, is it calling moduleBfunction or not. I tried to write a test using spyOn(). but i am not sure how can i mock a method inside a dependent module. I did some research and found i might be able to use 'rewire' module for this purpose like below
var moduleB = require('../moduleB');
moduleB.__set__('moduleBfunction', moduleBfunctionSpy);
moduleA.__set__('moduleB', moduleB);
it('should call moduleBfunction', function(){
moduleA.moduleAfunction()
expect(moduleB.moduleBfunction()).toHaveBeenCalled()
});
but I feel there should be a simpler way.
Please suggest.
I recommend sinon.js
var sinon = require('sinon')
var moduleA = require('../moduleA')
var moduleB = require('../moduleB')
it('should call moduleBfunction', function() {
var stub = sinon.stub(moduleB, 'moduleBfunction').returns()
moduleA.moduleAfunction()
expect(moduleB.moduleBfunction.calledOnce)
stub.restore()
})
You can easily fake many different behaviours like:
stub throws
stub returns a certain value
stub yields (mimicking async callback)
stub works restricted with just for certain input arguments
Don't forget to restore each stub before executing the next test. It's best to use sandboxes and afterEach / beforeEach
describe('tests which require some fakes', function() {
var sandbox
beforeEach(function() {
sandbox = sinon.sandbox.create()
})
afterEach(function() {
sandbox.restore()
})
})

How stub a global dependency's new instance method in nodejs with sinon.js

Sorry for the confusing title, I have no idea how to better describe it. Let's see the code:
var client = require('some-external-lib').createClient('config string');
//constructor
function MyClass(){
}
MyClass.prototype.doSomething = function(a,b){
client.doWork(a+b);
}
MyClass.prototype.doSomethingElse = function(c,d){
client.doWork(c*d);
}
module.exports = new MyClass();
Test:
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
})
I could get it working if .createClient('xxx') is called inside each method instead, where I stub client with:
var client = require('some-external-lib');
sinon.stub(client, 'createClient').returns({doWork:function(){})
But it feels wrong to init the client everytime the method each being called.
Is there a better way to unit test code above?
NEW: I have created a minimal working demo to demonstrate what I mean: https://github.com/markni/Stackoverflow30825202 (Simply npm install && npm test, watch the test fail.) This question seeks a solution make the test pass without changing main code.
The problem arises at the place of test definition. The fact is that in Node.js it is rather difficult to do a dependency injection. While researching it in regard of your answer I came across an interesting article where DI is implemented via a custom loadmodule function. It is a rather sophisticated solution, but maybe eventually you will come to it so I think it is worth mentioning. Besides DI it gives a benefit of access to private variables and functions of the tested module.
To solve the direct problem described in your question you can stub the client creation method of the some-external-lib module.
var sinon = require('sinon');
//instantiate some-external-lib
var client = require('some-external-lib');
//stub the function of the client to create a mocked client
sinon.stub(client, 'createClient').returns({doWork:function(){})
//due to singleton nature of modules `require('some-external-lib')` inside
//myClass module will get the same client that you have already stubbed
var MyClass = require('./myclass');//inside this your stubbed version of createClient
//will be called.
//It will return a mock instead of a real client
However, if your test gets more complicated and the mocked client gets a state you will have to manually take care of resetting the state between different unit tests. Your tests should be independent of the order they are launched in. That is the most important reason to reset everything in beforeEach section
You can use beforeEach() and afterEach() hooks to stub global dependency.
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
beforeEach(function () {
// Init global scope here
sandbox = sinon.sandbox.create();
});
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork').yield();
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
afterEach(function () {
// Clean up global scope here
sandbox.restore();
});
})
Part of the problem is here: var stub = sinon.stub(client,'doWork').yield();
yield doesn't return a stub. In addition, yield expects the stub to already have been called with a callback argument.
Otherwise, I think you're 95% of the way there. Instead of re-initializing for every test, you could simply remove the stub:
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce);
stub.restore();
})
})
BTW, another poster suggested using Sinon sandboxes, which is a convenient way to automatically remove stubs.

Stub out module function

Edit: Being a little bit more precise.
I want to test usecases for a Github API wrapper extension, that our team has created. For testing, we don't want to use API wrapper extension directly, so we want to stub out its functions. All calls to the API wrapper should be stubbed out for the tests, not just creating a clone stub.
I have a module "github" in Node.js:
module.exports = function(args, done) {
...
}
And I am requiring it like this:
var github = require('../services/github');
Now, I would like to stub out github(...) using Sinon.js:
var stub_github = sinon.stub(???, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});
But sinon.stub(...) expects from me to pass an object and a method and does not allow me to stub out a module that is a function.
Any ideas?
There might be a way to accomplish this in pure Sinon, but I suspect it would be pretty hacky. However, proxyquire is a node library that is designed for solving these sort of issues.
Supposing you want to test some module foo that makes use of the github module; you'd write something like:
var proxyquire = require("proxyquire");
var foo = proxyquire(".foo", {"./github", myFakeGithubStub});
where myFakeGithubStub can be anything; a complete stub, or the actual implementation with a few tweaks, etc.
If, in the above example, myFakeGithubStub has a property "#global" set as true, (i.e. by executing myFakeGithubStub["#global"] = true) then the github module will be replaced with the stub not only in the foo module itself, but in any module that the foo module requires. However, as stated in the proxyquire documentation on the global option, generally speaking this feature is a sign of poorly designed unit tests and should be avoided.
I found that this worked for me...
const sinon = require( 'sinon' );
const moduleFunction = require( 'moduleFunction' );
// Required modules get added require.cache.
// The property name of the object containing the module in require.cache is
// the fully qualified path of the module e.g. '/Users/Bill/project/node_modules/moduleFunction/index.js'
// You can get the fully qualified path of a module from require.resolve
// The reference to the module itself is the exports property
const stubbedModule = sinon.stub( require.cache[ require.resolve( 'moduleFunction' ) ], 'exports', () => {
// this function will replace the module
return 'I\'m stubbed!';
});
// sidenote - stubbedModule.default references the original module...
You have to make sure that you stub the module (as above) before it's required elsewhere...
// elsewhere...
const moduleFunction = require( 'moduleFunction' );
moduleFunction(); // returns 'I'm stubbed!'
Simplest solution is to refactor your module:
instead of this:
module.exports = function(args, done) {
...
}
do this:
module.exports = function(){
return module.exports.github.apply(this, arguments);
};
module.exports.github = github;
function github(args, done) {
...
}
Now you can require it with:
const github = require('../services/github.js');
//or
const github = require('../services/github.js').github;
To stub:
const github = require('../services/github.js');
let githubStub = sinon.stub(github, 'github', function () {
...
});
If you are doing
var github = require('../services/github');
in global scope, then you can using 'global' as the object and 'github' as the method to be stubbed out.
var stub_github = sinon.stub(global, "github", function (args, callback) {
console.log("the github(...) call was stubbed out!!");
});

Categories

Resources