Testing isomorphic-fetch used in ReactJs - javascript

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.

Related

How to mock a function with callback parameters in JestJS

My NodeJS app has a function readFilesJSON() that calls fs.readFile(), which of course invokes a callback with the parameters (err,data). The Jest unit test needs to walk both the error path and the data path.
My solution was to mock the call to fs.readFile() (see below). The mock function simply passes error or data based on test logic. This approach works when there is only one function being tested. The trouble I am seeing occurs when there are multiple functions that call fs.readFile(). Jest runs all the tests concurrently and the asynchronous nature of the functions mean that there is no guaranteed ordering to the calls to fs.readFile(). This non-deterministic behavior wrecks both the error/data logic and the parameter-checking logic using toHaveBeenCalledWith().
Does Jest provide a mechanism for managing independent usage of the mocks?
function readFilesJSON(files,done) {
let index = 0;
readNextFile();
function readNextFile() {
if( index === files.length ) {
done();
}
else {
let filename = files[index++];
fs.readFile( filename, "utf8", (err,data) => {
if(err) {
console.err(`ERROR: unable to read JSON file ${filename}`);
setTimeout(readNextFile);
}
else {
// parse the JSON file here
// ...
setTimeout(readNextFile);
}
});
}
}
}
The injected function setup looks like this:
jest.spyOn(fs, 'readFile')
.mockImplementation(mockFsReadFile)
.mockName("mockFsReadFile");
function mockFsReadFile(filename,encoding,callback) {
// implement error/data logic here
}
You can separate the different scenarios in different describe blocks and call your function after you clear the previous calls on the observed function, not to get false positive results.
import { readFile } from "fs";
import fileParser from "./location/of/your/parser/file";
jest.mock("fs");
// mock the file parser as we want to test only readFilesJSON
jest.mock("./location/of/your/parser/file");
describe("readFilesJSON", () => {
describe("on successful file read attempt", () => {
let result;
beforeAll(() => {
// clear previous calls
fileParser.mockClear();
readFile.mockImplementation((_filename, _encoding, cb) => {
cb(null, mockData);
});
result = readFilesJSON(...args);
});
it("should parse the file contents", () => {
expect(fileParser).toHaveBeenCalledWith(mockData);
});
});
describe("on non-successful file read attempt", () => {
let result;
beforeAll(() => {
// clear previous calls
fileParser.mockClear();
readFile.mockImplementation((_filename, _encoding, cb) => {
cb(new Error("something bad happened"), "");
});
result = readFilesJSON(...args);
});
it("should parse the file contents", () => {
expect(fileParser).not.toHaveBeenCalled();
});
});
});

How to mock a method that accepts no arguments and its supposed to work normally in 1 test, and supposed to throw an error in another test

I'm trying to get 100% coverage on my AWS project but I don't know how to mock methods that don't accept arguments AND are supposed to pass a test that makes them work properly(return values) and another test that makes them throw an error. I can't change the tech I am using so please try to help me with the things I am using right now.
I am using Nodejs, Typescript, Mocha, Chai, nyc and mock-require for mocking.
It's an AWS project so I am working with AWS methods
Here is the function and method, I am mocking describeAutoScalingGroups()
export async function suspendASGroups() {
const autoscaling = new AWS.AutoScaling();
const asgGroups = await autoscaling.describeAutoScalingGroups().promise();
if (!asgGroups.AutoScalingGroups) {
throw new Error("describeAutoScalingGroups inside of suspendAGSGroups didn't return any groups");
}
// some other stuff below
This is the TEST that is supposed to fail(Above this there is a test of the same function which will return regular values)
it('Should throw an error, should fail', async () => {
assertNative.rejects(awsFunctions.resumeAGSGroups());
try {
let result = await awsFunctions.suspendASGroups();
} catch (e) {
assert.isTrue(
e.name == 'Error' &&
e.message == "describeAutoScalingGroups inside of suspendAGSGroups didn't return any groups",
'describeAutoScalingGroups in suspendAGSGroups didnt have the proper error message'
);
}
});
And here is the mock code
public describeAutoScalingGroups() {
const data = (): AWS.AutoScaling.Types.AutoScalingGroupsType => {
return {
// some values here
};
return {
promise: data
};
}
I expect to be able to pass both tests, the one that expects a regular value and one that expects it to throw an error
here is a picture of the coverage: https://i.imgur.com/D6GX0tf.png
I expect that red part to be gone :)
Thank you
In your mock you need to return something for AutoScalingGroupsType that evaluates to false, since you have this check:
if (!asgGroups.AutoScalingGroups) { ... }
So you could simply do this:
public describeAutoScalingGroups() {
return {
promise: () => {
return { AutoScalingGroups: false }
}
};
I was given an answer on reddit so I will post it here too:
You need a different mock for each test. You should setup your mocks in before/beforeEach hooks which you will get from mocha https://mochajs.org/#hooks.
Using sinon would make the mock creation cleaner but if you are stuck with mock-require then it looks like you will need to use https://www.npmjs.com/package/mock-require#mockrerequirepath
--- end of comment
How I did it:
I made another mock file that is the same as the regular mock file, except this one can only fail functions(which is good)
Here is the code in the TEST:
describe('Testing FAILING suspendAGSGroups', () => {
it('Should throw an error, should fail', async () => {
const mock = require('mock-require');
mock.stopAll();
let test = require('./test/mocks/indexFailMocks');
mock.reRequire('./test/mocks/indexFailMocks');
let awsFailing = mock.reRequire('./handler');
// the line above is pretty important, without it It wouldnt have worked, you need to reRequire something even if it's the code of it isn't changed(I only changed the MOCK file but I had to reRequire my main function)
try {
let result = await awsFailing.suspendASGroups();
} catch (e) {
assert.isTrue(
e.name == 'Error' &&
e.message == "describeAutoScalingGroups inside of
suspendAGSGroups didn't return any groups",
'describeAutoScalingGroups in suspendAGSGroups didnt have the proper
error message'
);
}
});
});

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

How do I mock async fetches with jest?

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.

$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!

Categories

Resources