I have httpService that is an Observable, but just before returning, I'm turning it into a Promise with "lastValueFrom"
async getRandomRecipes(): Promise<BasicRecipe> {
const res = this.httpService
.get<ClientBasicRecipe>(url)
.pipe(
map((response) => {
return response.data;
}),
catchError((err) => throwError(() => new NotFoundException())), // Trying to test this exception
)
.pipe<BasicRecipe>(map((data) => this.getBasicRecipe(data)));
return lastValueFrom(res);
}
I want to test this exception with jest, but not sure how to do it. I know to use jest.spyOn on HttpService, but since it's an Observable; I don't know how to mock it properly.
This is what I have so far:
it("throw NotFoundException when there are no recipes", async ()=>{
jest
.spyOn(HttpService.prototype, 'get')
.mockImplementationOnce(() => throw new NotFoundException);
})
While working with Observables you need to understand that you can't rely on async/await to consider your test done. So, you can use the done parameter to handle that for you.
In your example, you will need to return an Observable within the mock implementation of the get function and, within that Observable you should throw an error.
Related
Here is the function under test:
export const callStoredProcedure = async (procedureName, procedureArgs)
=> {
let resultSet: any;
try {
await knexClient.transaction(async trx => {
await trx.raw(`CALL ${procedureName}(${procedureArgs});`).then(result => {
if (result[0] && result[0][0]) {
resultSet = JSON.stringify(result[0][0]);
}
});
});
} catch (err) {
console.log(err);
}
return resultSet;
};
And here I'm mocking the knexClient in a beforeEach block:
let knexClientMock;
beforeEach(() => {
// Stub Data source clients
const rawStub = sinon.stub().resolves([{ affectiveRows: 0, insertId: 0 }]);
knexClientMock = sinon.stub(knexClient, 'transaction').callsFake(
async (): Promise<any> => {
return {
raw: rawStub,
};
},
);
});
However, when I run my tests the if statement if (result[0] && result[0][0]) never runs and Jest coverage shows everything is uncovered from the raw statement down to the catch.
The mocking seems to be working correctly in that it uses the mock knexClient.transaction, but it leaves the rest of the lines uncovered. I think I just want to mock the raw function with a mock promise result and allow the .then function to run.
How can I mock the knexClient.transaction.raw function only and allow the rest of the code to have test coverage?
The issue is that your test stubs do not correctly mock the objects being used:
The "transaction" method in your code takes a single argument (a callback function) and invokes this callback, passing it a transaction executor that has an asynchronous "raw" method.
Your test stubs make the "transaction" method return an object that has an async raw method.
When writing unit tests for your code, it is very important to understand how your code works, first. Otherwise it is extremely easy to write meaningless tests that won't protect against introducing bugs.
I'm having a bit of trouble unmocking a function.
I first mock it and now I can't unmock it
//myClass.js
class myClass {
static check(v1,v2) {
return v1 > v2;
}
static async getinfo(v1,v2) {
if (this.check(v1,v2)) {
return await get('api.google.com');
}
return [];
}
}
//myclass.spec.js
describe('Testing myClass', () => {
describe('testing processing', () => {
it('should return result', () => {
const mockPatch = jest.fn().mockImplementation((version, solution) => false);
myClass.check = mockCheck;
try {
const result = await myClass.getinfo(1,2);
expect(result).toBe.([]);
}catch(e) {
throw e;
}
})
})
describe('Testing check', () => {
it('should return true', () => {
expect(myClass.check(2,1)).toBe.true
})
})
})
I already try with
myClass.check.mockRestore()
beforeEach(() => {myClass.check.mockRestore()})
jest.unmock('./myClass.js)
Is there anyway I can solve this? I read all the jest doc and i couldn't find anything
Methods should never be mocked by reassigning them, there is no way how Jest could restore their original implementation this way.
This should always be done with spyOn:
jest.spyOn(myClass, 'check').mockReturnValue(false)
This way a method can be restored with restoreMock or restoreAllMocks. This should be preferably enabled globally in Jest configuration.
I'm assuming that what you're hoping to do is to mock an implementation for use in a specific test, but then have your other tests function without the mocking.
If so, I think you could use the module mocking strategy in conjunction with mockReturnValueOnce.
Be sure to import your module at the top of your tests, then to call jest.mock with the same path. After that, you should be able to call myClass.check.mockReturnValueOnce, and it will be mocked until the next time it is called. After that, it will function normally 👍
I am fairly new to NestJS + Typescript + RxJs tech stack. I am trying to write a unit test case using Jest for one of my functions but not sure if doing it correctly.
component.service.ts
public fetchComponents(queryParams) {
const url = this.prepareUrl(queryParams);
const data$ = this.httpService.get(url);
return data$
.pipe(map(({ data }) => data));
}
component.sevice.spec.ts
Test case works and passes
describe('fetchComponents', () => {
const query = {
limit: 10,
offset: 0
};
const result: AxiosResponse = {
data: 'Components',
status: 200,
statusText: 'OK',
headers: {},
config: {}
};
it('should return Dummy Data when called successfully', () => {
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementation(() => of(result));
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
}
);
});
});
Can you please provide suggestions and pointers on how exactly I should test this function. Also without using Library like marbel-rx
I am not sure if I am testing it correctly. Is there something else also which I should test?
Since Observables are asynchronous, you have to add the asynchronous done paramter and call done() after the expect that is executed last. Otherwise, jest will finish the test run after subscribe() is called without waiting for the execution of the asynchronous execution of subscribe's callback. Try to make your test fail by for example by expecting 'Komponents'. The test will not fail.
Also, I'd recommend to use mockImplementationOnce instead of mockImplementation when possible, to avoid implicitly reusing mock behaviors in later calls and therewith creating implicit dependencies.
it('should return Dummy Data when called successfully', done => {
// Add done parameter ^^^^
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementationOnce(() => of(result));
// Prefer mockImplementationOnce ^^^^
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
done();
// ^^^^^^ Call done() when test is finished
}
);
});
I have the following async function that checks the returned value from a promise and I having trouble writing
async function fetchData(pageLocation) {
const data = await
apiService.fetchPage(pageLocation);
if (!data || !data.mapping) {
const error = new Error(`Unknown channel ${pageLocation}`);
error.code = 404;
throw (error);
}
return data.mapping;
}
Test case
describe.only('fetchData', () => {
let fetchPage;
beforeEach(() => {
fetchPage =
sinon.stub().returns(Promise.resolve(mockMapping));
csfPageService.__set__({
apiService: {
fetchPage,
},
});
});
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
expect(async () => { await fetchData('pageLocation'); }).to.throw();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
});
What causing the main issue is having an async function and a promise I am able to use the same approach when it is not an async function and there is no await I have looked into the following links
Catching thrown errors with SinonJS
https://www.chaijs.com/api/bdd/#method_throw
enter link description here
but I haven't been successful
please advise on how should this be done ...
That is one of the reasons I don't like async, await, they are just syntactic sugar over promises, but they uses normal/sync semantics but just in appearance.
Async functions never throws, no matter how bad is the error you throw inside it, they will just return a rejected promise. In your case, your function is not throwing at all, it is returning a rejected promise, and you are not attaching any catch hanlder to that promise, hence the warning. When you use async function or promises, forget about normal handling of errors, promises catches any error automatically and encapsulates them on a rejected promise.
So, in your case the correc way of doing this will vary depending on your testing framework, but it could be something like this:
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
fetchData('pageLocation').catch(err => {
expect(err).to.be.an.error();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
})
});
I'm trying to resolve multiple promises and return something when all of them have resolved
I've looked around but can't find a solution or I'm just not understanding.
My Code:
export const setLeagues = (res: any, leaguesArray: any) => {
leaguesArray.forEach((element: any) => {
firebaseAdmin.firestore().collection('leagues').add(element)
.catch((err: any) => { res.send(err) })
})
}
I want to do a res.send('Successfully Added!) when all promises in the forEach has resolved.
This is simple enough if you use Promise.all and map:
export const setLeagues = (res: any, leaguesArray: any) => {
// Wrap in a Promise.all, and use .map instead of .forEach:
Promise.all(leaguesArray.map((element: any) => {
// Make sure to return promise here...
return firebaseAdmin.firestore().collection('leagues').add(element)
.catch((err: any) => { res.send(err) })
})).then(() => {
// Do things here that need to wait for all promises to resolve.
})
}
I'll note that due to the way Promise.all works, you may want to change the way you handle an error as well. Promise.all rejects as soon as any of it's wrapped promises reject, or resolves when all of its wrapped promises resolve. So I would recommend moving your .catch to chain off the Promise.all rather than the inner promises to avoid sending a response twice:
export const setLeagues = (res: any, leaguesArray: any) => {
// Wrap in a Promise.all, and use .map instead of .forEach:
Promise.all(leaguesArray.map((element: any) => {
// Make sure to return promise here...
return firebaseAdmin.firestore().collection('leagues').add(element)
})).then(() => {
// Do things here that need to wait for all promises to resolve.
res.send('Successfully Added!')
}).catch((err: any) => {
// Consider moving error handler to here instead...
res.send(err)
})
}
You'll have to collect all your promises into and array and use Promise.all() to generate a new promise that's resolved only after each one of them is resolved. The general form is like this:
const promises = []
things.forEach(thing => {
promises.push(createPromiseForWork(thing))
})
const p = Promise.all(promises)
p.then(() => {
// continue with your work
})