How to mock variables in nodejs - node.js

When writing nodejs unit test case I want to mock variables.
let abc = awsMock.service.lambda;
In above code, awsMock I am creating and passing to method but service is json object which I want to mock. How I can pass some dummy value when above line is executed?
I want awsMock to be mock object only as I want to mock method using this after above code is executed.
I tried to stub variable but it didn't work

I recommend you encapsulate that clases who you need to mock in a service container and you can replace that values in your mock files.
I will use a minimal example case with three files:
- sc.js : The service container
- main.js: The main function
- test.js: The main function for tests
sc.js
// FAKE CLASS
class Lambda {
foo() {
return "real";
}
}
// END FAKE CLASS
// CODE START HERE
// The service container: could be an object, class, etc...
const serviceContainer = {
lambda: new Lambda(),
};
// Export SC
module.exports.sc = serviceContainer;
// Export properties with a wrap function (this is more easy to handle)
module.exports.getLambda = () => serviceContainer.lambda;
main.js
const { getLambda } = require("./sc");
module.exports.main = () => {
// Get lambda
const lambda = getLambda();
// Print foo()
console.log(lambda.foo());
}
test.js
const { sc } = require("./sc");
const { main } = require("./main");
// Exec before mock
main();
// Mock replace
sc.lambda = new class LambdaMock {
foo() {
return "mock";
}
}
// Exec after mock
main();
If you execute node test.js the output will be:
real
mock
So, you could use the getLambda() function in each file to get the Lambda Instance or the Mocked Lambda Instance.
NOTE: As a recommendation, you must wrap the AWS Lambda class in another class to make ensure the real and the mocked version will have the same functions.

Related

Jest - How do I mock a Service whose constructor takes arguments

I have this logic when starting NodeJS app:
const twiddleService = new twiddleService(
new twiddleClient(),
new UserRepository(),
new BusinessRepository()
);
const twiddleController = new twiddleController(twiddleService);
I've unit tested twiddleService by mocking twiddleClient.
Now I want to unit test twiddleController and mock twiddleService.
In my twiddleController.test.ts file I have
import { twiddleService } from '../../src/services/twiddle-service';
jest.mock('../../src/services/twiddle-service');
const twiddleController = new twiddleController(new twiddleService());
Obviously this doesn't work because twiddleService expects 3 arguments. I could mock twiddleClient and the repositories again, but ideally I wouldn't.
Basically the goal is I want to be able to do something like
jest.spyOn(TwiddleService, 'createBananas').mockResolvedValue('b');
So that I can unit test my controller.
What are best practices when it comes to solving this problem?
[Also I'm using typescript]
I don't think you need to import and call jest.mock on the twiddle-service at all.
Since you are using Dependency Injection to provide the twiddleService instance to the twiddleController constructor, your test can supply a simple object - conforming to the Twiddle Service's interface, of course - to the new twiddleController() call. You can use jest.fn with an implementation argument to define what gets returned by the service's createBananas method to the twiddleController instance. The resulting test would look something like the following:
describe("TwiddleController", () => {
test("calls createBananas method of twiddleService instance", () => {
const mockTwiddleService = {
createBananas: jest.fn(() => "b"),
};
const twiddleController = new TwiddleController(mockTwiddleService);
expect(twiddleController.doSomethingWithBananas()).toBe("b");
});
});

How to mock document.currentScript to return a valid script object with data attributes

I'm trying to write some tests in jest for an API that is intended to be shared with third parties. The inclusion method used by the third parties is a script tag which initiates the script but also passes along some data attributes via document.currentScript.
Here is a very cut down version of the code with the relevant parts:
// clientLib.js
export const getCurrentScript = () => document.currentScript;
export const initLib = () => {
const currentScript = getCurrentScript();
const reference = currentScript.getAttribute('data-reference');
const lang = currentScript.getAttribute('data-lang');
return new clientLib(reference, lang);
}
export class clientLib {
constructor(reference, lang) {
this._brand = reference;
this._market = lang;
}
}
window.clientLib = initLib();
// html
<script src="clientLib.js" data-reference="12345" data-lang="en-GB"></script>
What I'd like to be able to do in my test is something like this, but I've been unable to get anything to work:
// clientLib.test.js
import {
getCurrentScript,
initLib,
clientLib
} from './clientLib';
// here I want to mock the output of getCurrentScript() since document.currentScript does
// not exist, and I need the mock to return a script object with the two data attributes
// (ideally I need to be able to recreate this mock with both, either or none of the attributes)
// to test other cases
describe('initLib', () => {
it('returns a new instance of the library based on script attributes', () => {
window.clientLib = initLib();
// in here I should then be able to access properties on my lib on the window object
});
});
I did manage to get this working using an external setup file but for logistical reasons I don't think I can use this approach. I've tried to mock it in the test file itself and keep coming back to an "Invalid variable access: document" error. Is there a way to do this that I'm missing?

Is there possibillity to call constructor inside Container.get() in typedi?

I am using typedi with node.js
I wonder if I could do something like that
const obj = Container.get(MyObj(creds));
istead of
const obj = new MyObj(creds);
I need that to use because of jasmine unit testing. I need the same object.
How should I do it to get the same object inside my unit test?
#Service()
class MyObj {
#Inject("creds")
creds: any;
}
Then you can inject like this:
Container.set("creds", creds);
const obj = Container.get(MyObj);
console.log(obj.creds);

Assert that the function passed into a stubbed function is the correct function

I'm trying to test my Node module using Mocha.
The module is very small here is an example...
import { sharedFunctionA, sharedFunctionB, commonFunction } from <file>
const functionA = token => _data => sharedFunctionA(token);
const functionB = () => data => sharedFunctionB(data);
exports.doThingA = token => {
commonFunction(functionA(token));
};
exports.doThingB = () => {
commonFunction(functionB());
};
This is only a brief example but it shows what I'm trying to do.
I need to test that doThingA and doThingB pass in the correct function to the commonFunction.
I have stubbed the commonFunction and I can see that it is being called but I can't assert that the function passed in is correct.
TBH... I'm beginning to think of restructuring this entirely to pass in some sort of enum to the commonFunction and running the respective function from there.
In this case you can stub on the sharedFunctionA and sharedFunctionB and then retrieve the argument of your stub on the commonFunction and call it. Then check your other stubs are being called with the desired arguments.
I know it's tedious but it is the only way I can think of with your code.
Quick example:
const assert = require('assert')
const sinon = require('sinon')
const sharedFunctions = require('<fileWithSharedFunctions>')
const commonStub = sinon.stub(sharedFunctions, 'commonFunction')
const sharedBStub = sinon.stub(sharedFunctions, 'sharedFunctionB')
const fileToTest = require('<fileToTest>')
fileToTest.doThingB()
commonStub.getCall(0).args[0]()
assert(sharedBStub.calledOnce)

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();
});

Resources