Last active
November 2, 2017 16:49
-
-
Save sriramrudraraju/3596312c539d3958da51203a0f088b86 to your computer and use it in GitHub Desktop.
React Redux Unit Testing with Jest part 1: Action Creators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
// Actions for setting DOM properties | |
import { SET_H1, SET_TITLE } from './types'; | |
type DomActionType = { | |
type: string, | |
payload: string | |
}; | |
//Action for setting H1 element | |
export const setH1 = (text: string): DomActionType => { | |
return { type: SET_H1, payload: text }; | |
}; | |
//Action for setting title | |
export const setTitle = (text: string): DomActionType => { | |
return { type: SET_TITLE, payload: text }; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { setH1, setTitle } from './dom_actions'; | |
//test suite for dom actions | |
describe('dom_actions', () => { | |
//test suite for setH1 action creator | |
describe('setH1', () => { | |
//checking for its type | |
it('should have type of "SET_H1"', () => { | |
expect(setH1().type).toBe('SET_H1'); | |
}); | |
//checking for parameter passing as payload | |
it('should pass on to payload as we pass in params', () => { | |
let text = 'some random string'; | |
expect(setH1(text).payload).toBe(text); | |
}); | |
}); | |
//test suite for setTitle action creator | |
describe('setTitle', () => { | |
//checking fro its type | |
it('should have type "SET_TITLE"', () => { | |
expect(setTitle().type).toBe('SET_TITLE'); | |
}); | |
//checking for parameter passing as payload | |
it('should pass on to payload as we pass in params', () => { | |
let text = 'some random string'; | |
expect(setTitle(text).payload).toBe(text); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import type { | |
ActionAsyncType, | |
ActionType, | |
DispatchType | |
} from '../flow-types/actions'; | |
import 'whatwg-fetch'; | |
export default ( | |
url: string, | |
body: Object | null = null, | |
request: (() => ActionType) | null = null, | |
receive: ((Payload: any) => ActionType) | null = null, | |
error: ((Payload: string) => ActionType) | null = null, | |
pre: ((State: Object) => boolean) | null = null | |
): ActionAsyncType => { | |
// TODO: Does this return Promise<ActionType>? | |
return ( | |
dispatch: DispatchType, | |
getState: () => Object | |
): Promise<ActionType | void> => { | |
// If we have a pre-condition to fetching, check it. | |
// kind of cache, | |
if (pre) { | |
if (!pre(getState())) { | |
return Promise.resolve(); | |
} | |
} | |
// Action: Requesting data. | |
if (request) { | |
dispatch(request()); | |
} | |
// Fetch | |
let f = fetch( | |
url, | |
Object.assign({}, body) | |
).then((response: Object): Object => response.json()); | |
// Action: Receiving data. | |
if (receive) { | |
// Fix: Flow erroneously believes `receive` to potentially be null at this point. | |
const r = receive; | |
f = f.then((data: Object): ActionType => dispatch(r(data))); | |
} | |
// Action: Error receiving data. | |
if (error) { | |
// Fix: Flow erroneously believes `error` to potentially be null at this point. | |
const r = error; | |
f = f.catch((e: string): ActionType => dispatch(r(e.toString()))); | |
} | |
return f; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import type { | |
ActionAsyncType, | |
ActionNoPayloadType, | |
ActionPayloadObjectType, | |
ActionPayloadStringType | |
} from "../../flow-types/actions"; | |
import type { StateSurveysType } from "../../flow-types/redux"; | |
//we created seperate function to make the fetch fetch calls depending on the requests | |
import asyncActionCreator from "../async-action-creator"; | |
export const requestSurveyList = (): ActionNoPayloadType => { | |
return { type: "REQUEST_SURVEY_LIST" }; | |
}; | |
export const receiveSurveyList = (payload: Object): ActionPayloadObjectType => { | |
return { type: "RECEIVE_SURVEY_LIST", payload: payload }; | |
}; | |
export const surveyListError = (payload: string): ActionPayloadStringType => { | |
return { type: "SURVEY_LIST_ERROR", payload: payload }; | |
}; | |
export const fetchSurveyList = (): ActionAsyncType => { | |
return asyncActionCreator( | |
"http://blahblagblah", | |
{ | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json; charset=utf-8" | |
} | |
}, | |
requestSurveyList, | |
receiveSurveyList, | |
surveyListError, | |
(state: StateSurveysType): boolean => { | |
return !state.surveys.surveyList.length; | |
} | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import configureMockStore from "redux-mock-store"; | |
import thunk from "redux-thunk"; | |
import { fetchSurveyList } from "../surveys/list"; | |
//test suite for surveys/list actions | |
describe("survey-list", () => { | |
let middlewares, mockStore, mockResponse, store; | |
beforeEach(() => { | |
//since we are using thunk as middleware, so load it | |
middlewares = [thunk]; | |
//mockStore mocks the store behaviour | |
mockStore = configureMockStore(middlewares); | |
//instead calling api we will be calling mocks to get the response | |
//generic mock response function will be as below | |
mockResponse = (status, statusText, response) => { | |
return new window.Response(response, { | |
status: status, | |
statusText: statusText, | |
headers: { | |
"Content-type": "application/json" | |
} | |
}); | |
}; | |
}); | |
//when the action creator call the api and gets a success response with no cache | |
it("calls request with no cache and fetch response was successful", () => { | |
//creating fake fetch with success response | |
global.fetch = jest | |
.fn() | |
.mockImplementation(() => | |
Promise.resolve( | |
mockResponse(200, null, '{"ids":{"provider":' + "123" + "}}") | |
) | |
); | |
//setting the cache condition to false by making empty | |
store = mockStore({ | |
surveys: { | |
surveyList: [] | |
} | |
}); | |
//trigger the action | |
return store.dispatch(fetchSurveyList()).then(() => { | |
//all the consequent actions will be available at .getActions() | |
const expectedActions = store.getActions(); | |
//sice it has request action and request sucess, so length will be 2 | |
expect(expectedActions.length).toBe(2); | |
// last action should be success action, since we not calling other actions | |
expect(expectedActions[length + 1].type).toBe("RECEIVE_SURVEY_LIST"); | |
}); | |
}); | |
//call the action and response is failure with nocache | |
it("calls request with no cache and fetch response was error", () => { | |
//creating fake fetch with error response | |
global.fetch = jest | |
.fn() | |
.mockImplementation(() => | |
Promise.reject( | |
mockResponse(500, null, '{"ids":{"provider":' + "123" + "}}") | |
) | |
); | |
//setting the cache condition to false by making empty | |
store = mockStore({ | |
surveys: { | |
surveyList: [] | |
} | |
}); | |
//trigger the action | |
return store.dispatch(fetchSurveyList()).then(() => { | |
//all the consequent actions will be available at .getActions() | |
const expectedActions = store.getActions(); | |
//sice it has request action and request error, so length will be 2 | |
expect(expectedActions.length).toBe(2); | |
// last action should be error action, since we not calling other actions | |
expect(expectedActions[length + 1].type).toBe("SURVEY_LIST_ERROR"); | |
}); | |
}); | |
//when trying to call the api with a cache | |
it("calls request with cache and fetch response was success", () => { | |
//creating fake fetch with success response | |
global.fetch = jest | |
.fn() | |
.mockImplementation(() => | |
Promise.resolve( | |
mockResponse(500, null, '{"ids":{"provider":' + "123" + "}}") | |
) | |
); | |
// setting some random cache for making cache condition to be true | |
store = mockStore({ | |
surveys: { | |
surveyList: ["some", "random", "values"] | |
} | |
}); | |
//trigger the action | |
return store.dispatch(fetchSurveyList()).then(() => { | |
//all the consequent actions will be available at .getActions() | |
const expectedActions = store.getActions(); | |
//because of cache, no actions will be called, it will be 0 | |
expect(expectedActions.length).toBe(0); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment