How do I mock async fetches with jest? - javascript

I've been struggling to figure out how to properly test this code for days :(
const request = require('superagent');
const getDog = () => {
return request.get('https://dog.ceo/api/breeds/image/random');
};
it('resolves', () => {
// Expect mocked response
});
it('rejects', () => {
// Expect mocked response
});

In most cases, your code gets some value from API parses it and makes some stuff with it.
As a result, you don't want to make real API call and mock it instead.
There are a couple of ways to do it. One of the possible is to mock the only method on the superagent library.
// tell jest not to mock superagent because we'll mock the only method
jest.unmock('superagent');
const request = require('superagent');
const getDog = () => {
return request.get('https://dog.ceo/api/breeds/image/random');
};
it('resolves', () => {
// mock the get request to resolve object
request.get = jest.fn().mockResolvedValue({
message: 'Your message'
});
// Expect mocked response
expect.assertions(1);
return expect(getDog()).resolves.toEqual({
message: 'Your message'
});
});
it('rejects', () => {
// mock the get request to reject object
request.get = jest.fn().mockRejectedValue({
message: 'Your error'
});
// Expect mocked response
expect.assertions(1);
return expect(getDog()).rejects.toEqual({
message: 'Your error'
});
});
I used expect.assertions(1), there is a reason:
This is often useful when testing asynchronous code, in order to make
sure that assertions in a callback actually got called.
There are links that can help you:
mockFn.mockResolvedValue(value)
,
.rejects

One solution is to use a library such as nock or fetch-mock to mock out the HTTP response to your request.

Related

Mocha/Chai/Supertest passing tests when not meeting requirements [duplicate]

I'm using Mocha and SuperTest to test my Express API. However my first test always seems to pass when inside the .then() of my request().
I'm passing in a String to a test that is expecting an Array. So should definitely fail the test.
It fails outside of the then() as expected, but I won't have access to the res.body there to perform my tests.
Here is my code:
const expect = require('chai').expect;
const request = require('supertest');
const router = require('../../routes/api/playlist.route');
const app = require('../../app');
describe('Playlist Route', function() {
// before((done) => {
// }
describe('Get all playlists by user', function() {
it('Should error out with "No playlists found" if there are no Playlists', function() {
request(app).get('/api/playlists/all')
.then(res => {
const { body } = res;
// Test passes if expect here
expect('sdfb').to.be.an('array');
})
.catch(err => {
console.log('err: ', err);
});
// Test fails if expect here
expect('sdfb').to.be.an('array');
})
})
});
I found this article but I'm not using a try catch block, but I thought maybe it could have something to do with the promise.
Quick reponse
it('decription', function(done) {
asyncFunc()
.then(() => {
expect(something).to.be(somethingElse)
done()
})
})
Detailed response in the comment of #jonrsharpe
Rather than using done, simply return request(app).get('/api/playlists/all') since request() returns a promise. Since you have expect('sdfb').to.be.an('array'); twice, remove the one that's not in the .then callback. When using asynchronous code, remember that synchronous code that appears to come after the async chain will execute before the promise .then handlers. This is counterintuitive.
Here's the .then approach:
it('should ...', () => {
return request(app)
.get('/api/playlists/all')
.then(res => {
const {body} = res;
// assert here
});
});
The other approach is to await the promise yourself in the test case function, then make assertions on the resolved response object. In this case, drop the then chain. This approach is generally preferred as it reduces nesting.
it('should ...', async () => {
const res = await request(app).get('/api/playlists/all');
const {body} = res;
// assert here
});
If you don't let Mocha know you're working with asynchronous code by returning a promise, awaiting the promises, or adding and calling the done parameter, the assertions occur asynchronously after the test is over and disappear into the void, creating a false positive.
Skip .catch either way. Since you've informed Mocha of the promise, if it rejects, it'll let you know.

Stubbing function to return something acceptable for .promise()

I'm running tests and I'm stubbing a function that calls the AWS sqs.deleteMessage function. .promise() is called on the call to this function. Every time I run my tests with the coverage I notice that it jumps to the catch block thus an error must be occurring on my .promise() call.
I've tried stubbing the function to resolve the promise but that doesn't seem to work. I've tried returning data as well and still have the same issue.
Below is an example of the code I'm trying to test. It never reaches the logger.info() line
fooObj.js
const foo = async (req) => {
try{
let res = await bar.deleteMessage(handle).promise();
logger.info("Sqs result message " + JSON.stringify(res));
} catch(error){
#catch block code
}
}
Below is the code for bar.deleteMessage()
bar.js
const aws = require('aws-sdk');
const sqs = new aws.SQS();
deleteMessage = function(handle){
return sqs.deleteMessage({
ReceiptHandle: handle
});
}
And finally here is the test code
const fooObj = require('foo')
const barObj = require('bar')
jest.mock('bar')
describe('foo test', ()=>{
test('a test' , ()=>{
barObj.deleteMessage.mockImplementation(()=>{
return Promise.resolve({status:200})
});
return fooObj.foo(req).then(data=>{
#Expect statements here
})
}
}
So I would like the logger.info line to be reached in coverage but I assume the issue has to do with how I'm stubbing the bar.deleteMessage function. I could use the aws-sdk-mock but I feel like I'm violating unit testing principles by mocking the sqs call that is in another file and the proper way to do it would simply be to properly stub the bar.deletemessage() function
You just need one change:
bar.deleteMessage needs to return an object with a promise property set to the function that returns the Promise:
barObj.deleteMessage.mockImplementation(() => ({
promise: () => Promise.resolve({ status: 200 })
}));
...or you can shorten it to this if you want:
barObj.deleteMessage.mockReturnValue({
promise: () => Promise.resolve({ status: 200 })
});

Testing a Jest method that has multiple promises

I am attempting to write a test for a service in my app.
async post(url, params, headers) {
const csrfToken = await this.getCsrfToken().then(res => res.data);
headers.headers['X-CSRF-TOKEN'] = csrfToken;
// console.log(params);
return this.http.post(url, params, headers);
}
The issue I am encountering is I am getting an error that data is not defined. I believe this refers to the csrfToken call (which is just another API call to get this token to append to the header).
I'm not entirely sure how to mock that constant inside jest so I can actually get to my post call. Is there an easy way in jest?
You shouldn't try to mock the constant, you should mock the getCsrfToken instead. Try something like:
import { getCsrfToken, post } from MyClass
it('should work', () => {
// mock method on your class
myMock = jest.fn()
myMock.mockReturnValueOnce(Promise.resolve({
data: {
fakeCsrf
}
})
MyClass.csrfToken = myMock
post('/test', {}, {})
expect(...);
});

$httpBackend doesn't seem to be flushing requests

I am testing my Angular app using ngDescribe. I don't think ngDescribe should be too much of a problem here, as it's just managing dependency injection for me. I first began to attempt my test the way the ngDescribe docs say, in the code below I have changed it to a more direct approach just to see if I could get any changes. I am calling a method that in turn calls $http.post('urlname', data); While debugging I can clearly see that my method gets all the way to where it calls post() but then it never continues. Because of this, my test always times out.
Hopefully I've just got something simple that's wrong! Here is my code. The test that fails is "Should work", all the others pass as expected.
Please also note that this is being processed by babel, both the test and the service, before being tested.
Here is the service, it works perfectly when being used. It has a few other variables involved that I have removed, but I assure you those variables are working correctly. While debugging for the tests, I can see that the await is hit, but it never continues past that, or returns. When used in the app, it returns exactly as expected. I THINK this has something to do with ngmock not returning as it should.
async function apiCall (endPoint, data) {
if (!endPoint) {
return false;
}
try {
return data ? await $http.post(`${endPoint}`, data) : await $http.get(`${endPoint}`);
} catch (error) {
return false;
}
}
Here are the tests:
ngDescribe({
name: 'Api Service, apiCall',
modules: 'api',
inject: ['apiService', '$httpBackend'],
tests (deps) {
let svc;
beforeEach(() => {
svc = deps.apiService;
});
it('is a function', () => {
expect(angular.isFunction(svc.apiCall)).toBe(true);
});
it('returns a promise', () => {
const apiCall = svc.apiCall();
expect(angular.isFunction(apiCall.then)).toBe(true);
});
it('requires an endpoint', async () => {
const apiCall = await svc.apiCall();
expect(apiCall).toBe(false);
});
it('should work', (done) => {
deps.http.expectPOST('fakeForShouldWork').respond({ success: true });
const apiCall = svc.apiCall('fakeForShouldWork', {});
apiCall.then(() => done()).catch(() => done());
deps.http.flush();
});
},
});
The method being called, apiCall, is simply a promise that is resolved by $http.post().then(); It will also resolve false if an error is thrown.
Since deps.http.expectPOST does not fail, I can tell that the outgoing request is sent. I validated this by changing it to expectGET and then I received an error about it being a POST.
I have tried moving the flush() method to all different parts of the test method, but it seems to make no difference.
Any thoughts? Thanks so much for your help!

Testing isomorphic-fetch used in ReactJs

I have the following function and i want to test it using mocha and chai, expect.
Here "fetch" and "dispatch" is a function. "fetch" is used to do ajax calls to the server
function fetchMetadata() {
return (dispatch) => {
fetch('/json/metadata').then(
(result) => {
if (result.status === 200) {
return result.json();
}
throw "request failed";
}
).then(
(jsonResult) => {
dispatch(jsonResult);
}
);
}
}
I am trying to test is as following but i cant get the test to fail as the error is swallowed by the promise!
"fetchMock" is a function which mocks "fetch" function so that we dont actually make a call to server to get the data back.
describe('Should work', ()=>{
before(()=>{
fetchMock.mock('^/json/metadata', {body1:"testing"})
});
it.only('should fetch metadata when fetchMetadata is called ', function(){
let returnedFetchMetadataFn = fetchMetadata();
let mockDispatch = (argument)=>{
expect(argument).toBe('SET_METADATA1');
};
returnedFetchMetadataFn(mockDispatch);
});
});
What am i doing wrong or what am i missing??
Look into the promise-sync library.
It is a synchronous mock for promises and it allows to specify which assert errors to ignore inside the success/error/finally callbacks.

Categories

Resources