action 构造函数测试
- 测试actions.js
import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js';
let nextTodoId = 10;
export const addTodo = (text) => ({
type: ADD_TODO,
completed: false,
id: nextTodoId ++,
text: text
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
id: id
});
export const removeTodo = (id) => ({
type: REMOVE_TODO,
id: id
});
- actions.test.js
import * as actions from '../../src/todos/actions.js';
import * as actionTypes from '../../src/todos/actionTypes.js';
describe('todos/actions', () => {
describe('addTodo', () => {
const addTodo = actions.addTodo
it('should create an action to add todo', () => {
const text = 'first todo';
const action = addTodo(text);
expect(action.text).toBe(text);
expect(action.completed).toBe(false);
expect(action.type).toBe(actionTypes.ADD_TODO);
});
it('should have different id for different actions', () => {
const text = 'first todo';
const action1 = addTodo(text);
const action2 = addTodo(text);
expect(action1.id !== action2.id).toBe(true);
});
});
});
异步action构造函数测试
- action.js
import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js';
export const fetchWeatherStarted = () => ({
type: FETCH_STARTED
});
export const fetchWeatherSuccess = (result) => ({
type: FETCH_SUCCESS,
result
})
export const fetchWeatherFailure = (error) => ({
type: FETCH_FAILURE,
error
})
export const fetchWeather = (cityCode) => {
return (dispatch) => {
const apiUrl = `/data/cityinfo/${cityCode}.html`;
dispatch(fetchWeatherStarted())
return fetch(apiUrl).then((response) => {
if (response.status !== 200) {
throw new Error('Fail to get response with status ' + response.status);
}
response.json().then((responseJson) => {
dispatch(fetchWeatherSuccess(responseJson.weatherinfo));
}).catch((error) => {
dispatch(fetchWeatherFailure(error));
});
}).catch((error) => {
dispatch(fetchWeatherFailure(error));
})
};
}
- actions.test.js
import thunk from 'redux-thunk';
import {stub} from 'sinon';
import configureStore from 'redux-mock-store';
import * as actions from '../../src/weather/actions.js';
import * as actionTypes from '../../src/weather/actionTypes.js';
const middlewares = [thunk];
const createMockStore = configureStore(middlewares);
describe('weather/actions', () => {
describe('fetchWeather', () => {
let stubbedFetch;
const store = createMockStore();
beforeEach(() => {
stubbedFetch = stub(global, 'fetch');
});
afterEach(() => {
stubbedFetch.restore();
});
it('should dispatch fetchWeatherSuccess action type on fetch success', () => {
const mockResponse= Promise.resolve({
status: 200,
json: () => Promise.resolve({
weatherinfo: {}
})
});
stubbedFetch.returns(mockResponse);
return store.dispatch(actions.fetchWeather(1)).then(() => {
const dispatchedActions = store.getActions();
expect(dispatchedActions.length).toBe(2);
expect(dispatchedActions[0].type).toBe(actionTypes.FETCH_STARTED);
expect(dispatchedActions[1].type).toBe(actionTypes.FETCH_SUCCESS);
});
});
});
});
reducer测试
import * as actions from '../../src/weather/actions.js';
import * as actionTypes from '../../src/weather/actionTypes.js';
import * as Status from '../../src/weather/status.js';
import reducer from '../../src/weather/reducer.js';
describe('weather/reducer', () => {
it('should return loading status', () => {
const action = actions.fetchWeatherStarted();
const newState = reducer({}, action);
expect(newState.status).toBe(Status.LOADING);
});
});
无状态React组件测试
- 对于一个无状态的React组件,可以使用Enzyme的shallow方法来渲染,因为shallow方法只渲染一层,所以不会牵涉子组件的React组件渲染
- filter.js
import React from 'react';
import Link from './link.js';
import {FilterTypes} from '../../constants.js'
import './style.css';
const Filters = () => {
return (
<p className="filters">
<Link filter={FilterTypes.ALL}> {FilterTypes.ALL} </Link>
<Link filter={FilterTypes.COMPLETED}> {FilterTypes.COMPLETED} </Link>
<Link filter={FilterTypes.UNCOMPLETED}> {FilterTypes.UNCOMPLETED} </Link>
</p>
);
};
export default Filters;
- filters.test.js
import React from 'react';
import {shallow} from 'enzyme';
import Filters from '../../../src/filter/views/filters.js';
import Link from '../../../src/filter/views/link.js';
import {FilterTypes} from '../../../src/constants.js';
describe('filters', () => {
it('should render three link', () => {
const wrapper = shallow(<Filters />);
expect(wrapper.contains(<Link filter={FilterTypes.ALL}> {FilterTypes.ALL} </Link>)).toBe(true);
expect(wrapper.contains(<Link filter={FilterTypes.COMPLETED}> {FilterTypes.COMPLETED} </Link>)).toBe(true);
expect(wrapper.contains(<Link filter={FilterTypes.UNCOMPLETED}> {FilterTypes.UNCOMPLETED} </Link>)).toBe(true);
});
});
被链接的React组件测试
- 所有有状态的React组件都是通过connect函数产生的组件,被称为“被连接的组件”
import React from 'react';
import {mount} from 'enzyme';
import {createStore, combineReducers} from 'redux';
import {Provider} from 'react-redux';
import {reducer as todosReducer, actions} from '../../../src/todos/index.js';
import {reducer as filterReducer} from '../../../src/filter/index.js';
import {FilterTypes} from '../../../src/constants.js';
import TodoList from '../../../src/todos/views/todoList.js';
import TodoItem from '../../../src/todos/views/todoItem.js';
describe('todos', () => {
it('should add new todo-item on addTodo action', () => {
const store = createStore(
combineReducers({
todos: todosReducer,
filter: filterReducer
}), {
todos: [],
filter: FilterTypes.ALL
});
const subject = (
<Provider store={store}>
<TodoList />
</Provider>
);
const wrapper = mount(subject);
store.dispatch(actions.addTodo('write more test'));
expect(wrapper.find('.text').text()).toEqual('write more test');
});
});