How to test class with chain promises using jest? - javascript

I have use case where i have to test promise chaining so in below code i tried to mock all the function that are being called in actual code and using jest.fn() but getting some error TypeError: Cannot read property 'then' of undefined
any what is implemented wrong in below code or any better approach to write async promises chaining ?
main.ts
export class ModuleExecutor {
public execute(moduleName: string): (param1, param2) => any {
const self = this;
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;
// #ts-ignore - red squiggly for the return mismatch which are resolved in Grunt
return mod.init(getHttpModule(self.identity), params);
})
.then((httpRequest: HttpRequestBase) => {
_httpRequest = httpRequest;
if (_mod.Response().Summary.Header) {
throw _mod.Response().Summary;
}
return httpRequest;
})
.then((params1: any) => {
_params = params2;
_mod.ExecStage = ExecStage.Core;
return _mod.core(_params, _httpRequest);
})
.catch((e) => {
return e;
});
};
}
}
main.spec.ts
import {ModuleExecuter} . from './main.ts';
const _executer = new ModuleExecutor(Identity.node);
const myMockFunc = jest.fn(() => {
// _executer.execute("Payments/accountBalance/GetAccount001");
executer = new ModuleExecutor(Identity.node);
executerSpy = executer.execute("Payments/accountBalance/GetAccount001");
const _promise1 = new Promise(function(resolve) {
// moduleExecutor.execute(params, callback function)
executerSpy(params, function(data) {
resolve(data);
}).catch((e) => {
expect(e).toBeTruthy();
});
});
Promise.resolve(_promise1);
});
const myMockFuncExecute = jest.fn(() => {
Promise.resolve(getApiModule(Identity.node, "Payments/accountBalance/GetAccount"));
});
const myMockModuleBase = jest.fn((mod: ModuleBase<any, any>) => {
// _executer.execute("Payments/accountBalance/GetAccount001");
mod.ExecStage = ExecStage.Init;
// #ts-ignore - red squiggly for the return mismatch which are resolved in Grunt
Promise.resolve(mod.init(getHttpModule(Identity.node), params));
});
const myMockHttpRequest = jest.fn((httpRequest: HttpRequestBase) => {
Promise.resolve(httpRequest);
});
it('should your test scenario', (done) => {
myMockFunc()
.then((data) => {
expect(myMockFuncExecute).toBeCalledWith("Payments/accountBalance/GetAccount001");
expect(myMockModuleBase).toBeCalledWith(ModuleBase);
expect(myMockHttpRequest).toBeCalledWith(HttpRequestBase);
expect(data).toEqual({test: "test"});
done();
});
});

Related

Node.js fs.access returns undefined no matter what

Here's the code:
import * as path from 'path';
import * as fs from 'fs';
const doesPathExist = async (path: string) => {
return await fs.access(path, fs.constants.R_OK, (err) => {
return err ? false : true;
});
};
const someFunc = async (documentName: string) => {
const documentPath = path.join(__dirname, documentName);
const pathExists = doesPathExist(documentName);
};
The function doesPathExistseems to return Promise<void>, making the pathExists variable undefined no matter the outcome. I've tried initializing a temp variable at the top of the function before running fs.access and changing its value inside the callback but still no luck.
The issue is with fs.access, this function does not return a Promise or anything else.
There are a few options to solve it.
you can always use fs.accessSync()
const doesPathExist = async (path: string) => {
try {
fs.accessSync(path, fs.constants.R_OK)
return true
} catch (e) {
return false
}
};
Use fs.promises
const fsPromises = fs.promises;
const doesPathExistB = async (path: string) => {
try {
await fsPromises.access(path, fs.constants.R_OK)
return true
} catch (e) {
return false
}
};
// OR
const doesPathExistA = async (path: string) => {
return new Promise(async (resolve) => {
await fsPromises.access(path, fs.constants.R_OK)
.then(() => resolve(true))
.catch(() => resolve(false))
})
};

Function created using "crypto" won't return the bearerToken I created with it

I am sure this is something basic that I am missing but it's late and my brain gets like that. So I have this function:
export const createRandomHex = () => {
try {
return crypto.randomBytes(127, (_err, buf) => {
console.log("[create] bearerToken: ", buf.toString("hex"));
const bearerTokenString = buf.toString("hex");
return bearerTokenString;
});
} catch (e) {
return e;
}
};
And I am calling it in another function as such:
export const createBearerToken = () => {
const bearerToken = createRandomHex();
}
For some reason, the value of bearerToken in the createBearerToken is undefined or null. Can anyone help me with this?
randomBytes is asynchronous and takes a callback. You could return a promise in your function:
export const createRandomHex = async () => {
return new Promise((resolve, reject) => {
try {
return crypto.randomBytes(127, (_err, buf) => {
console.log("[create] bearerToken: ", buf.toString("hex"));
const bearerTokenString = buf.toString("hex");
resolve(bearerTokenString);
});
} catch (e) {
reject(e);
}
});
};

How to test javascript class using jest?

trying to test class using jest its throwing declaration exception , what would be correct approach to test class in below scenario and test promise chain?
main.ts
export class ModuleExecutor {
public execute(moduleName: string): (param1, param2) => any {
const self = this;
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;
// #ts-ignore - red squiggly for the return mismatch which are resolved in Grunt
return mod.init(getHttpModule(self.identity), params);
});
}
}
}
main.spec.ts
describe("Test promise chaining with Mock functions", () => {
const myMockFuncExecute = jest.fn((mod: ModuleBase<any, any>) => {
_mod = mod;
_mod.ExecStage = ExecStage.Init;
const ret = _mod.Init(getHttpModule(Identity.node), params);
Promise.resolve(ret);
});
jest.mock("./main.ts");
const executerClass = require("./main.ts");
executerClass.mockImplementation(() => {
return {
init: myMockFuncExecute
};
});
it('it should get Modulebase', async () => {
const _result = new executerClass(Identity.node);
_result.execute("Payments/url").then(myMockFuncExecute())
.then((module) => {
const mod = module;
expect(mod).toEqual(ModuleBase);
});
});
});

How can I write a unit test with streams, promises and pipes together?

My function is
exports.downloadFromBucket = function(fileKey) {
const localPath = `${process.cwd()}/data/${fileKey}`
return new Promise((resolve, reject) => {
const localFile = fs.createWriteStream(localPath)
const awsStream = s3.getObject({
Bucket: process.env.UPLOAD_BUCKET,
Key: fileKey
})
.createReadStream()
.on('error', (err) => {
logger.info('Error downloading file', err)
return reject(err)
})
.on('finish', () => {
logger.info('Completed downloading')
return resolve(localPath)
})
.pipe(localFile)
})
}
How would I go about writing a unit test for this using mocha and sinon?
This may not be the prettiest solution but assuming you want to mock s3 and fs and test the on('error') and on('finish') behavior:
You could use a custom s3 mocking class, stub the original s3 and fs with sinon and trigger the events you want to test.
// Custom S3 Mocking Library
class S3MockLibrary {
constructor() {
this.events = {};
}
getObject(options) {
return this;
}
createReadStream() {
return this;
}
on(event, func) {
this.events[event] = func;
return this;
}
pipe(file) {
return this;
}
emit(event, err) {
this.events[event](err);
}
}
Test on('finish')
it('should verify', async () => {
const s3Mock = new S3MockLibrary();
const fsStub = sinon.stub(fs, 'createWriteStream').returns('success');
const s3Stub = sinon.stub(s3, 'getObject').returns(s3Mock);
// Emit the finish event async
setTimeout(() => {
s3Mock.emit('finish');
}, 0);
const result = await downloadFromBucket('test');
fsStub.restore();
s3Stub.restore();
sinon.assert.calledOnce(fsStub);
sinon.assert.calledOnce(s3Stub);
assert.equal(result, `${process.cwd()}/data/test`);
});
Test on('error)
it('should fail', async () => {
const s3Mock = new S3MockLibrary();
const fsStub = sinon.stub(fs, 'createWriteStream').returns('success');
const s3Stub = sinon.stub(s3, 'getObject').returns(s3Mock);
setTimeout(() => {
s3Mock.emit('error', 'testError');
}, 0);
let error;
await downloadFromBucket('test').catch((err) => {
error = err;
});
fsStub.restore();
s3Stub.restore();
sinon.assert.calledOnce(fsStub);
sinon.assert.calledOnce(s3Stub);
assert.equal(error, 'testError');
});

Jest/enzyme: How to test for .then and .catch of an nested asynchronous function

I'm trying to do some enzyme/jest unit testing for a asynchronous function in my reactJS component, which gets injected as prop.
My Problem is to test for a value in the then() part of the function and to test for catch() if an error occures.
This is how the function of my component (<CreateAccount />) looks like:
_onSubmit = (event) => {
event.preventDefault()
const { username, password } = this.state
this.props.createUserMutation({
variables: { username, password }
}).then(response => {
const token = response.data.createUser.token
if (token) {
Cookies.set('auth-token', token, { expires: 1 })
}
}).catch(error => {
console.warn(error)
})
}
The first test should check for .catch(error => {}) as data is undefined:
it('_onSubmit() should throw error if data is missing', () => {
const createUserMutation = () => {
return Promise.resolve({})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
expect(wrapper).toThrow() // <-- How to do it correctly?
})
And the second test should check if cookie is set correctly. Here I don't know how to do that? I think I have to mock Cookie
it('_onSubmit() should get token', () => {
const createUserMutation = () => {
return Promise.resolve({
data: {
createUser: { token: 'token' }
}
})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
})
// How to check for token value and Cookies?
})
What I usually have to do when I want to see if the spy worked on the catch or then, is to add another then() on the test. For example:
it('_onSubmit() should throw error if data is missing', () => {
const createUserMutation = jest.fn(() => Promise.reject(new Error()));
const spy = jest.spyOn(console,"warn");
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
});
return createUserMutation.catch(() => {
expect(wrapper).toMatchSnapshot();
})
.then(() => {
expect(spy).toHaveBeenCalledTimes(1);
});
})
I guess it is somehow related to how NodeJS handles the queues, promises, ticks, etc, internally.
That is the rejected/catch branch. If you want to test the IF path, just use a Promise.resolve and return a promise.then() instead of catch.
Why are you using console.warn for an error? Use console.error instead. You will need to mock it out to a spy as well to test it.
First test:
it('_onSubmit() should throw error if data is missing', (done) => {
const createUserMutation = () => new Promise();
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.update().find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
createUserMutation.resolve().then(() => {
expect(console.warn).toHaveBeenCalled();
done();
});
})
If you are running this in a mock browser environment and not a real browser then you must mock out Cookies.set.
Second test:
it('_onSubmit() should get token', (done) => {
const createUserMutation = () => new Promise();
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
});
jest.spyOn(window.Cookies, 'set');
const response = {
data: {
createUser: { token: 'token' }
}
}
createUserMutation.resolve(response).then(() => {
expect(window.Cookies.set).toHaveBeenCalled();
done();
});
})
afterEach(() => {
// Reset the spies so that they don't leak to other tests
jest.restoreAllMocks();
});

Categories