how can I test this redux action in jest? - javascript

I want to test this redux action which uses axios, I have succesfully tested the success path but I can't reach the catch block to complete the code coverage. I'm mocking axios using a mocks folder in jest.
export const fetchTasks = () => dispatch => {
dispatch(fetchLoading());
axios
.get(url + '/all', { mode: 'cors' })
.then(response => {
dispatch(setTasks(response.data));
dispatch(fetchDone());
})
.catch(response => {
console.log(response);
dispatch(fetchDone());
});
};
this is my success path test which implements a redux store and expects loading and setTasks to run and the setTasks action to have a payload that matches my mockObjects tasks list.
describe('when fetchTasks is dispatched', () => {
it('should dispatch other actions with correct payloads', async () => {
const store = testStore();
const spyOnDispatch = jest.spyOn(store, 'dispatch');
await tasksActions.fetchTasks()(store.dispatch, store.getState);
expect(spyOnDispatch).toHaveBeenCalledWith({ type: 'FETCH_LOADING' });
expect(spyOnDispatch).toHaveBeenCalledWith({ type: 'SET_TASKS', tasks: mockObjects.mockTasks });
expect(spyOnDispatch).toHaveBeenCalledWith({ type: 'FETCH_DONE' });
expect(store.getState().tasksState.tasks).toEqual(mockObjects.mockTasks);
});
});
I've tried to use this code to make the catch code to run but it executes the same code as the success path and sets the tasks list to undefined
mockAxios.get.mockImplementationOnce(() => Promise.reject({ status: 400 }));

When testing frontend code that makes HTTP request, I suggest using a mocking library to create fake responses. Two that I am familar with are nock and fetch-mock. You should be able to throw an error in your mock response code so that it triggers the catch() branch.

Related

Error handling API calls with axios Interceptors. Am I doing this right?

Hello I'am completly new with React/Redux so there is a possibility that I violated some principles with the below code , so bare with me.
I'm building a React App which will consume my Express API. Everything is working perfectly but when I was building the Action Creators I couldnt think of a good way to handle any errors coming from the API without wrapping every single axios request with try/catch blocks.
Both in PHP world where I come from and Express you can create a global Error handler.
For any async requests in my Express APP I wrap them with the below function so I can catch them the same way as the synchronous.
module.exports = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch((err) => next(err));
};
};
From what I've learned through googling is that, there is an ErrorBoundary HOC for handling errors inside Components and for axios calls I should use axios interceptors. So I created this:
AxiosFactory Class
import axios from "axios";
import { setError } from "../actions/utilActions";
import store from "../store";
class AxiosFactory {
constructor(baseURL) {
this.instance = axios.create({
baseURL,
});
this.instance.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
// Getting the errors from Express API
const {
response: {
data: { errors },
},
} = error;
store.dispatch(setError(errors));
return Promise.reject(error);
}
);
}
getInstance() {
return this.instance;
}
}
export default AxiosFactory;
User API Caller
import AxiosFactory from './AxiosFactory';
const axios = new AxiosFactory('/api/v1/users/').getInstance();
export default axios;
User ActionCreator
import { SUCCESS_LOGIN } from "./types/userTypes";
import userApi from "../apis/user";
// Tries to login the user
export const signInUser = () => {
return async (dispatch) => {
// Test
const {data:{data:{user} = await userApi.post("login", {
email: "test#test.com",
password: "test12345!",
});
dispatch({
type: SUCCESS_LOGIN,
payload: user,
});
}
Error ActionCreator
import { HAS_ERROR } from "./types/utilTypes";
export const setError = (errors) => {
return async (dispatch) => {
dispatch({
type: HAS_ERROR,
payload: errors,
});
};
};
The interceptor dispatches succesfuly the setError and the error state is getting updated like a charm, which means I dont need to manual dispatch on each call. Although I still need to catch the Promise rejection from Interceptor.
My 2 questions are:
Is there a way to lets say "stop the dispatch from executing" inside my User ActionCreator without try/catching the Promise ?
Does this whole thing I setup makes sense ? Or there is a better way to do it?

Cancel old Promise.resolve if same method called multiple times with given timeframe

I am using Promise and axios in react to call POST api and fetch list of records.
Issue is when multiple API calls triggered then any one which response last is getting is updating state.
Where i want to use only last called API response.
Exp : call API 3 times with different postbody, in case first call response take time than 2nd & 3rd then callback is using response of 1st call to setstate, instead i want to forget 1 and second call and consider last call response only.
Following is example
Common File
const apiService = axios.create({
baseURL: 'https://example.com/api/,
});
function post(postData) {
return Promise.resolve(apiService.post('https://example.com/api/getuserlist', postData, {
headers: {'Authorization': `Bearer sdfsdfsdf-cvdfs`}
}));
}
Service File
static getUsers(postData) {
return post(postData);
}
React Component
function getUsersList = (Filters )=>{
getUsers({ "Filters": Filters }).then((response) => {
this.setState({ users: response.data})
})
}
Problem is when getUsersList get called multiple time whichever is last response is getting set to users state, where last call should be users list.
It's not yet possible to actually cancel promises in JavaScript (though there is a proposal for it).
However, it is possible to implement the functionality you want in React. Consider something like this:
// state shape:
// {
// loading: boolean
// apiPromise: null | Promise<{ data: User[] }>
// users: User[]
// }
getUsersList = async (Filters) => {
// raw promise - don't await it yet
const apiPromise = getUsers({ Filters })
this.setState({ apiPromise, loading: true })
// _here_ we await it
const response = await apiPromise
// check if it's the most recent API request we made
if (this.state.apiPromise === apiPromise) {
this.setState({ users: response.data, loading: false })
} else {
console.log("stale val discarded:", users)
}
}
CodeSandbox demo - simplified example with mocked API and single val rather than list of users. Try clicking the button many times in quick succession and watch the console output.
Using CPromise wrapper the code might look like this See the live demo:
const CPromise = require('c-promise2');
const axios= require('axios');
// Let's wrap axios get method to the CPromise
function get(url){
return new CPromise((resolve, reject, {onCancel})=>{
axios.get(url, {
cancelToken: new axios.CancelToken(function executor(cancel) {
onCancel(cancel)
})
}).then(resolve, reject);
});
}
let chain= null;
function makeRequest(url){
chain && chain.cancel();
chain= get(url).then((response)=> {
console.log(`Response ${JSON.stringify(response.data)}`);
}, function (err) {
console.warn(`Error: ${err}`);
}
);
}
// some endpoint with a delay of 3 seconds for a response
const url= "https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=3s";
makeRequest(url);
// make the same request again, abort the previous
setTimeout(()=> makeRequest(url), 1000);
But since the package is in early beta stage, you can use the plain cancellation token, provided by axios See Axios documentation

Node.js - How to test HTTPS requests with promise

I started writing unit tests for my nodeJs application so i could learn about this concept.
After writing some basic tests for simple functions (using Mocha and Chai) i want to move on to some more complex tests.
I have written a simple piece of code that can make a request using node's HTTPS module. That code looks like this:
const https = require('https')
module.exports.doRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
Now i want to invoke this method with the following parameters:
const PARAMS = {
host: 'jsonplaceholder.typicode.com',
port: 433,
method: 'GET',
path: `/todos/1`,
headers: {
Authorization: 'Bearer 123'
}
}
And make the request like so:
getTodos = (PARAMS) => {
return doRequest(PARAMS).then((result) => {
if (result.errors) { throw result }
return {
'statusCode': 200,
'body': JSON.stringify({ message: result.title }),
}
}).catch((error) => ({
'statusCode': error.statusCode,
'body': JSON.stringify({ message: error.message }),
}
))
}
Now my question is how i can test this bit of code properly. I have looked on how to tackle this with the Nock.js libary but i don't have a good understanding on where to start.
If anyone can point me in the right direction on how to start with writing some tests for this bit of code i will be thankfull.
In general, you would want to black box your HTTP handling, so that as few modules in your application need to care about the details of HTTP as possible.
In the source folder, you'd have one module (e.g. commonhttp.js). You want this to export your HTTP functions, and other modules in your application use them like this:
const commonhttp = require('./commonhttp');
commonhttp.doRequest( ... ).then( ... );
Other modules, like todos.js, and various other modules, will export their own functions using that module, for example:
const commonhttp = require('./commonhttp');
const todos = {
getTodos( ... ) {
return commonhttp.doRequest( ... );
},
createTodo( ... ) {
return commonhttp.doRequest( ... );
},
// etc.
};
module.exports = todos;
For your unit tests, when you test the todos.js module, you want to mock any calls to the commonhttp module; you can use simple mocha + Sinon for this, and spy on the doRequest method. Basically all you're testing is "when I call getTodos, I expect it to make a call to doRequest with these arguments". You'd follow this pattern for all the modules in your application that uses doRequest.
You also, of course, want to test the commonhttp module -- that spec is where Nock might come in handy. It's not strictly necessary, you can also "block-box" the http module, but you have to set up a lot of complicated spies to mimic the behavior of http; instead, writing a spec (using Nock) that says "ok, I call doRequest with these params, that should have made this HTTP call" does make sense.

Download file using redux flow

Could not find any example describing it.
I'm trying to create download button using react app but going through redux, ie. in action I'm connecting to url and getting the file in response (code 200).
export function sendTimeline(token, timelineObj) {
const request = axios.post(`muURLGoesHere`, timelineObj, {
headers: {
"Content-Type": "application/vnd.ms-excel",
"X-Accept-Version": "v1",
Authorization: `Bearer ${token}`
}
});
console.log(request);
return {
type: constants.actionTypes.TIMELINE_DATA,
payload: request
};
}
How can I pass it to reducer and download it on the react side.
You maybe know the article with an example or any hint on that.
Thank you
I wouldn't dispatch actions with promised as payload. Use redux-thunk additionally to do more fine grained dispatch of successive actions:
const sendTimeline = (token, timline) => dispatch => {
dispatch({ type: 'sendtimeline/start' })
return fetch(…)
.then(res => res.json())
.then(json => dispatch({
type: 'sendtimeline/success',
payload: json
}))
.catch(err => dispatch({
type: 'sendtimeline/error',
payload: err,
error: true
}))
}
That way you do not have to deal with async problems in the reducer, since the actions handle that. Also you can do stuff like:
this.props.sendTimeline(…).then(…)
when used within a component, since the promise is returned from the action creator.
Have a look here as well

Mock Remote HTTP Response in Tests

I'm attempting to write a test that checks the behaviour of my app depending on the response received from an API. To do this, I'm trying to make the response to the request I make have the config that I need.
An example is checking to see that my app redirects to the home page after logging in. I need the response to be HTTP 200 and have any value for an API key.
I am using axios to make the requests
Currently I have tried the following libraries with no success
moxios
axios-mock-adapter
nock
Does anyone have any experience with mocking remote HTTP requests and their responses?
EDIT: If it helps, I am using Mocha and Chai for tests
it('Does stuff', function () {
moxios.stubRequest('/auth/get_api_key', {
status: 200,
responseText: 'hello'
})
return this.app.client.setValue('[name="username"]', 'testing').then(() => {
return this.app.client.setValue('[name="password"]', 'testing').then(() => {
return this.app.client.submitForm('#login-form').then(() => {
return this.app.client.getRenderProcessLogs().then(function (logs) {
console.log(logs)
})
})
})
})
})
The code above is what I'm using to see if the request goes through and it outputs this
[ { level: 'SEVERE',
message: 'http://127.0.0.1:8000/auth/get_api_key/ - Failed to load resource: net::ERR_CONNECTION_REFUSED',
source: 'network',
timestamp: 1510082077495 } ]
My example was mostly taken from the moxios docs.
First in your test import the libraries you need. Afterwards, you'll handle creating and destroying the mock axios server in your beforeEach and afterEach hooks. You'll then pass a sinon.spy() into axios' get request you want to spy on.
This get request will be mocked by moxios and you can access it through moxios.wait(() => { moxios.request.mostRecent(); }); Inside the wait() method you can set the response you want on the most recent requests.
Afterwards, just like when you use axios you'll use the then() function to get your response. Inside the then() you can create your tests for that specific response. When you're done with your specs call done(); to close the request and move the specs along.
import axios from 'axios';
import moxios from 'moxios';
import sinon from 'sinon';
import { equal } from 'assert'; //use the testing framework of your choice
beforeEach(() => {
moxios.install();
});
afterEach(() => {
moxios.uninstall();
});
it('Does stuff', (done) => {
moxios.withMock(() => {
let onFulfilled = sinon.spy();
axios.get('/auth/get_api_key').then(onFulfilled);
moxios.wait(() => {
let request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: {
apiKey: 1234567890
}
}).then((res) => {
equal(onFulfilled.called, true);
equal(res.status, 200);
equal(res.response.apiKey, 1234567890);
done();
})
})
})
});

Categories

Resources