How to test function that return promises? - javascript

trying to write unit test using jest it never get into test case where i retruned promise so i can get the response after then.
if there is any better way of writing test cases for promises i would appreciate that input.
executer.ts
export class Executor {
private passedParam: ILogParams = {} as ILogParams;
constructor(public identity: Identity) {
this._ajv = new Ajv();
}
public execute(moduleName: string): (param1, param2) => any {
const self = this;
// getting rid of the tslint issue with Function
return function(params: any, responseCallback: (param: any , param2: any) => any) {
let _mod;
let _httpRequest;
let _params;
Promise.resolve(getApiModule(self.identity, moduleName))
.then((mod: ModuleBase<any, any>) => {
_mod = mod;
mod.ExecStage = ExecStage.Init;
return mod.init(getHttpModule(self.identity), params);
})
.then((httpRequest: HttpRequestBase) => {
_httpRequest = httpRequest;
if (_mod.Response().Summary.Header) {
throw _mod.Response().Summary;
}
return httpRequest;
})
};
}
}
executer.spec.ts
import {ModuleExecutor} from "../../src/ts/common/executor";
it('should transition with the correct event', (done) => {
const executer = new ModuleExecutor(Identity.node);
const executerSpy = executer.execute("Payments/accountBalance/GetAccountBalance");
new Promise((resolve, reject) => {
resolve(true);
}).then(((mod: ModuleBase<any, any>) => {
done();
}))
.catch((error) => {
done();
});
});

Related

How to test function defined on window object in Jest and typescript

I'm trying to test a call to a function defined on the global window object. I have looked at multiple examples but am still not able to run a simple test case.
Api.ts
import "./global.d";
const verifier = window.Verifier;
export class Api {
constructor(private readonly id: string) {}
public fetchData() {
return new Promise<object>((resolve, reject) => {
verifier.request({
request: {
id: this.id
},
onSuccess: (data: object) => {
resolve(data);
},
onFailure: () => {
reject("Error!");
}
});
});
}
}
Api.test.ts
import { Api } from "./Api";
let windowSpy: any;
describe("Test Apis", () => {
beforeEach(() => {
windowSpy = jest.spyOn(window, "window", "get");
});
afterEach(() => {
windowSpy.mockRestore();
});
it("should call the function", () => {
const mockedReplace = jest.fn();
windowSpy.mockImplementation(() => ({
Verifier: {
request: mockedReplace
}
}));
const api = new Api("123");
api.fetchData();
expect(mockedReplace).toHaveBeenCalled();
});
});
global.d.ts
import { Verifier } from "./verifier";
declare global {
interface Window {
Verifier: Verifier;
}
}
verifier.d.ts
type RequestPayload = {
request: {
id: string;
};
onSuccess: (data: object) => void;
onFailure: () => void;
};
type verifyCode = number;
export interface Verifier {
request: (requestPayload: RequestPayload) => verifyCode;
}
I have also created a codesandbox example for easy reference
https://codesandbox.io/s/cranky-mccarthy-2jj622?file=/src/verifier.d.ts
The problem here is your import order.
When you import { Api } from "./api";, you run const verifier = window.Verifier;, which, at the time, is undefined.
If you change the order of the imports and spies it should work as expected:
import { RequestPayload } from "./verifier";
const mockRequest = jest
.fn()
.mockImplementation((requestPayload: RequestPayload) => 1);
jest.spyOn(window, "window", "get").mockImplementation(() => {
return {
Verifier: {
request: mockRequest,
},
} as unknown as Window & typeof globalThis;
});
// // // // //
import { Api } from "./api";
// // // // //
describe("Test Apis", () => {
let api: Api;
beforeEach(() => {
jest.clearAllMocks();
});
beforeEach(() => {
api = new Api("123");
});
it("should have valid function", () => {
expect(typeof api.fetchData).toBe("function");
});
it("should call the function", () => {
api.fetchData();
expect(mockRequest).toHaveBeenCalled();
});
});
You could also think about using window.Verifier directly, making the tests a bit cleaner:
export class Api {
constructor(private readonly id: string) {}
public fetchData() {
return new Promise<object>((resolve, reject) => {
window.Verifier.request({
request: {
id: this.id,
},
onSuccess: (data: object) => {
resolve(data);
},
onFailure: () => {
reject("Error!");
},
});
});
}
}
describe("Test Apis", () => {
let api: Api, mockRequest: jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
mockRequest = jest
.fn()
.mockImplementation((requestPayload: RequestPayload) => 1);
jest.spyOn(global, "window", "get").mockImplementation(() => {
return {
Verifier: {
request: mockRequest,
},
} as unknown as Window & typeof globalThis;
});
});
beforeEach(() => {
api = new Api("123");
});
it("should have valid function", () => {
expect(typeof api.fetchData).toBe("function");
});
it("should call the function", () => {
api.fetchData();
expect(mockRequest).toHaveBeenCalled();
});
});

expect(jest.fn()).toHaveBeenCalled() fails even though the function has been called

I'm trying to unit test a function that returns a promise. I'm having challenges verifying if
the mocked functions are called. Here's what I've done.,
// codetotest.js
const { SomeModule, isSomething, isSomethingElse } = require("some-module");
exports.somefunction = (param1, param2)=> {
const someModule = new SomeModule();
someModule.someMethod("aaa", isSomething);
someModule.someMethod("bbb", isSomethingElse);
return (someModule.someOtherMethod(param1)
.then(()=>{someModule.run(param2)}));
}
And this is the test file, the test says the mocked functions are not called, but I do see the console statement in the mock function is being displayed.
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", () => {
return {
SomeModule: jest.fn().mockImplementation(() => {
return {
someMethod: jest.fn((param, fn) => { console.log("This prints!"); }),
someOtherMethod: jest.fn((param) => { return Promise.resolve(() => { }) }),
run: jest.fn((param) => { return Promise.resolve(() => { return []; }) })
}
})
};
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
const someModule = new SomeModule();
let output = await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(someModule.someMethod).toHaveBeenCalled(); // This fails
await expect(someModule.someOtherMethod.mock.results[0]).resolves;
expect(someModule.someOtherMethod).toHaveBeenCalled(); // This fails
await expect(someModule.run.mocks.results[0]).resolves;
expect(someModule.run).toHaveBeenCalled(); // This fails
});
});
Appreciate any help/pointers.
Thank you.
P.S: I'm still a beginner when it comes to nodeJs development and unit testing.
I spent quite some time on this and finally figured the instantiated mock class didn't return the mocked methods properly. This answer gave me a hint on where I was going wrong.
So accordingly, I had to change my test file as follows.,
// codetotest.test.js
const { somefunction} = require("./codetotest.js");
const { SomeModule } = require("some-module");
jest.mock("some-module", function() {
return {
SomeModule: jest.fn().mockImplementation(function() { // Arrow function cannot be used as constructor
// Because I was not using the 'this' operator, my constructor always returned empty
this.someMethod = jest.fn((param, fn) => { console.log("This prints!"); });
this.someOtherMethod = jest.fn((param) => { return Promise.resolve(() => { }) });
this.run = jest.fn((param) => { return Promise.resolve(() => { return []; }) });
return {
someMethod: this,someMethod,
someOtherMethod: this.someOtherMethod,
run: this.run
}
})
};
});
afterEach(() => {
jest.restoreAllMocks();
});
describe("Test codetotest.js", () => {
it("somefunction() - success", async () => {
await somefunction("param1", "param2");
expect(SomeModule).toHaveBeenCalled();
expect(SomeModule.mock.instances[0].someMethod).toHaveBeenCalled(); // This works
expect(SomeModule.mock.instances[0].someOtherMethod).toHaveBeenCalled(); // This works
expect(someModule.mock.instances[0].run).toHaveBeenCalled(); // This works
});
});

Javascript - serialize async operations

My connections manager is expected to receive connect requests nondeterministically and to handle them in sequence.
How can asynchronous operations be queued such that they are handled later?
I'm not sure of what objects to place in a queue.
Here is my beginner's attempt. Thanks for any help!
class RequestQueue {
private _requests = [];
constructor() {}
public add( rq: any ) {
this._requests.unshift(rq);
}
public remove() {
return this._requests.pop();
}
}
class ConnectionManager {
private requestQueue;
private connecting: boolean = false;
constructor() {
this.requestQueue = new RequestQueue();
}
// ConnectionManager is expected to receive connection requests
// nondeterministically and to handle them in sequence.
public connect(id: string) {
if (!this.connecting) {
this.connecting = true;
return this.asyncConnect(id)
.then(
(result) => result,
(err) => err
)
.then( () => {
this.connecting = false;
if (this.requestQueue.length > 0) {
return this.requestQueue.remove();
}
});
} else {
// how do I queue a someAsyncOp for later?
}
}
private asyncConnect(id: string) : Promise<any> {
return new Promise( (resolve, reject) => {
console.log('begin connect to ', id);
setTimeout(() => {
console.log('end connect to ', id);
resolve();
}, 3000);
});
}
}
function makeConnections() {
const connectionManager = new ConnectionManager();
connectionManager.connect('A');
connectionManager.connect('B');
connectionManager.connect('C');
connectionManager.connect('D');
}
makeConnections();
https://codepen.io/cssbog/pen/BaBMzWW
You have to implement the async serializer pattern
The solution is to keep a queue of Promises that chains them one after the other. It is just a few lines of code and it is a general purpose, allowing any function to be serialized:
const serialize = (fn) => {
let queue = Promise.resolve();
return (...args) => {
const res = queue.then(() => fn(...args));
queue = res.catch(() => {});
return res;
};
};

Mock stream finish on jest

I have a function that downloads a file using got and uses fs.createWritableStream to write file on disk. The code is working smooth but the unit test are being a pain:
download(twilioPath, destFile) {
return new Promise(function(resolve, reject) {
const uri = `${TWILIO_BASE_URL}${twilioPath}`.replace('json', 'mp3')
let file = fs.createWriteStream(`/tmp/${destFile}`)
console.log(got)
let str = got.stream(uri)
console.log(uri)
str.on('error', function(err) {
console.log('Error dl', err)
reject(err)
})
str.pipe(file)
file.on('finish', function() {
console.log('banana')
let cb = function() {
console.log('Download completed')
resolve(file)
}
file.close(cb)
})
})
My resolve or reject is not being called and I don't know how to fix that:
const Recording = require('./Recording')
const Readable = require('stream')
var mockedStream = new Readable();
var mockedWrite = new Readable.Writable()
jest.mock('fs', () => ({
createWriteStream: jest.fn((file_name) => {
return mockedWrite;
})
}));
jest.mock('got', () => {
return {
stream: () => {
return mockedStream
}
}
})
describe('Recording', () => {
test('should download a file from twilio', async () => {
...
mockedStream.emit('error')
mockedWrite.emit('finish')
console.log(result)
....
})
})
I've tried to force emit events but no lucky.

js get class of calling object in function

I have a structure similar to this:
class Person {
greet() {
console.log(this.constructor.name)
}
}
class User extends Person {
}
let user = new User()
user.greet()
Unfortunately it prints window for this.constructor.name instead of User.
Is there some other way to get the actual class Name?
Actual code:
static MeteorMethod(target: MeteorModel, key: string, descriptor: PropertyDescriptor) {
let constructor = target.constructor
let className = target.constructor.name
let methodName = key
let method = descriptor.value
let meteorMethodName = MeteorModelDecorators.generateMethodName(constructor, methodName)
MeteorModelDecorators.MeteorMethodClasses[className] = target
if(Meteor.isServer) {
Meteor.methods({
[meteorMethodName]: (modelClassName: string, modelProps: object, ...args: any[]) => {
let model = new MeteorModelDecorators.MeteorMethodClasses[modelClassName](modelProps)
method.apply(model, args)
}
})
}
else {
descriptor.value = async function(...args: any[]) {
// here I expect this to be Book, but I get Window
return new Promise(function(resolve, reject) {
Meteor.call(meteorMethodName, this.constructor.name, this, args, (error: any, result: any) => {
if(error) reject(error)
resolve(result)
})
})
}
}
}
class MeteorModel {
#MeteorMethod
save() {
console.log('save')
}
}
class Book extends MeteorModel {
}
let book = new Book()
book.save()
Your problem is in this part:
descriptor.value = async function(...args: any[]) {
// here I expect this to be Book, but I get Window
return new Promise(function(resolve, reject) {
Meteor.call(meteorMethodName, this.constructor.name, this, args, (error: any, result: any) => {
if(error) reject(error)
resolve(result)
})
})
}
It needs to be this:
descriptor.value = async function(...args: any[]) {
// With the arrow function, should be Book
return new Promise((resolve, reject) => {
Meteor.call(meteorMethodName, this.constructor.name, this, args, (error: any, result: any) => {
if(error) reject(error)
resolve(result)
})
})
}
The function you were passing to the Promise constructor was setting up a new context, by using the arrow function you pick up the this context from the surrounding method.

Categories

Resources