Testing Redux

master
Tudor Stanciu 2020-04-12 03:11:18 +03:00
parent 9416e917f8
commit f9ff64efd4
6 changed files with 165 additions and 2 deletions

View File

@ -5,7 +5,7 @@ import AboutPage from "./about/AboutPage";
import Header from "./common/Header"; import Header from "./common/Header";
import PageNotFound from "./PageNotFound"; import PageNotFound from "./PageNotFound";
import CoursesPage from "./courses/CoursesPage"; import CoursesPage from "./courses/CoursesPage";
import ManageCoursePage from "./courses/ManageCoursePage"; import ManageCoursePage from "./courses/ManageCoursePage"; // eslint-disable-line import/no-named-as-default
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";

View File

@ -9,7 +9,13 @@ import { newCourse } from "../../../tools/mockData";
import Spinner from "../common/Spinner"; import Spinner from "../common/Spinner";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
function ManageCoursePage({ courses, authors, actions, history, ...props }) { export function ManageCoursePage({
courses,
authors,
actions,
history,
...props
}) {
const [course, setCourse] = useState({ ...props.course }); const [course, setCourse] = useState({ ...props.course });
const [errors, setErrors] = useState({}); const [errors, setErrors] = useState({});
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);

View File

@ -0,0 +1,34 @@
import React from "react";
import { mount } from "enzyme";
import { authors, newCourse, courses } from "../../../tools/mockData";
import { ManageCoursePage } from "./ManageCoursePage";
function render(args) {
const defaultProps = {
authors,
courses,
// Passed from React Router in real app, so just stubbing in for test.
// Could also choose to use MemoryRouter as shown in Header.test.js,
// or even wrap with React Router, depending on whether I
// need to test React Router related behavior.
history: {},
actions: {
saveCourse: jest.fn(),
loadAuthors: jest.fn(),
loadCourses: jest.fn()
},
course: newCourse,
match: {}
};
const props = { ...defaultProps, ...args };
return mount(<ManageCoursePage {...props} />);
}
it("sets error when attempting to save an empty title field", () => {
const wrapper = render();
wrapper.find("form").simulate("submit");
const error = wrapper.find(".alert").first();
expect(error.text()).toBe("Title is required.");
});

View File

@ -0,0 +1,52 @@
import * as courseActions from "./courseActions";
import * as types from "./actionTypes";
import { courses } from "../../../tools/mockData";
import thunk from "redux-thunk";
import fetchMock from "fetch-mock";
import configureMockStore from "redux-mock-store";
// Test an async action
const middleware = [thunk];
const mockStore = configureMockStore(middleware);
describe("Async Actions", () => {
afterEach(() => {
fetchMock.restore();
});
describe("Load Courses Thunk", () => {
it("should create BEGIN_API_CALL and LOAD_COURSES_SUCCESS when loading courses", () => {
fetchMock.mock("*", {
body: courses,
headers: { "content-type": "application/json" }
});
const expectedActions = [
{ type: types.BEGIN_API_CALL },
{ type: types.LOAD_COURSES_SUCCESS, courses }
];
const store = mockStore({ courses: [] });
return store.dispatch(courseActions.loadCourses()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
});
describe("createCourseSuccess", () => {
it("should create a CREATE_COURSE_SUCCESS action", () => {
//arrange
const course = courses[0];
const expectedAction = {
type: types.CREATE_COURSE_SUCCESS,
course
};
//act
const action = courseActions.createCourseSuccess(course);
//assert
expect(action).toEqual(expectedAction);
});
});

View File

@ -0,0 +1,51 @@
import courseReducer from "./courseReducer";
import * as actions from "../actions/courseActions";
it("should add course when passed CREATE_COURSE_SUCCESS", () => {
// arrange
const initialState = [
{
title: "A"
},
{
title: "B"
}
];
const newCourse = {
title: "C"
};
const action = actions.createCourseSuccess(newCourse);
// act
const newState = courseReducer(initialState, action);
// assert
expect(newState.length).toEqual(3);
expect(newState[0].title).toEqual("A");
expect(newState[1].title).toEqual("B");
expect(newState[2].title).toEqual("C");
});
it("should update course when passed UPDATE_COURSE_SUCCESS", () => {
// arrange
const initialState = [
{ id: 1, title: "A" },
{ id: 2, title: "B" },
{ id: 3, title: "C" }
];
const course = { id: 2, title: "New Title" };
const action = actions.updateCourseSuccess(course);
// act
const newState = courseReducer(initialState, action);
const updatedCourse = newState.find((a) => a.id == course.id);
const untouchedCourse = newState.find((a) => a.id == 1);
// assert
expect(updatedCourse.title).toEqual("New Title");
expect(untouchedCourse.title).toEqual("A");
expect(newState.length).toEqual(3);
});

20
src/redux/store.test.js Normal file
View File

@ -0,0 +1,20 @@
import { createStore } from "redux";
import rootReducer from "./reducers";
import initialState from "./reducers/initialState";
import * as courseActions from "./actions/courseActions";
it("Should handle creating courses", function () {
// arrange
const store = createStore(rootReducer, initialState);
const course = {
title: "Clean Code"
};
// act
const action = courseActions.createCourseSuccess(course);
store.dispatch(action);
// assert
const createdCourse = store.getState().courses[0];
expect(createdCourse).toEqual(course);
});