Reactはともかく、最近はReduxはReact contextもあるからか、記事が少なくてテストについてもどうやれば良いのか悩みました。
テストについてはまだ学び始めたばかりなので、基本的なテストの内容についてまとめました。
今回は簡単なTodoリストを使ってテストについてまとめます。
コードはこちらに上げています。
Reduxのreducer周りのテスト
今回作成したReducerはこちらです。
import { combineReducers, createSlice } from '@reduxjs/toolkit'
const initialState = {
count: 0,
todo: []
}
const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {
addTodo: (state, action) => {
state.todo.push({id: state.count++, text: action.payload, completed: false})
},
deleteTodo: (state, action) => {
const index = state.todo.findIndex(i => i.id === action.payload);
if(index !== -1) {
state.todo.splice(index,1);
}
},
completeTodo: (state, action) => {
const todo = state.todo.find(i => i.id === action.payload);
if(todo !== undefined) {
todo.completed = !todo.completed;
}
}
}
})
export const selectTodo = state => state.todo;
export const rootReducer = combineReducers({todo: todoSlice.reducer})
export const {addTodo, deleteTodo, completeTodo} = todoSlice.actions;
export default todoSlice.reducer;
こちらをテストします。
src
直下のディレクトリに___test__
という名前のフォルダを作成して、テストファイルを格納します。it()
内に書いたテストごとにテストケースとして認識されます。今回割と名前は適当にしてしまいましたが、it("should add todo text to the state",()=>{})
とかshould...
としてどういう内容のテストをするのか書いたほうが分かりやすくなりますね。
このTodoリストは、画面上のinputからtodoを「追加」、「削除」、「完了」とすることが出来るreducerを作成したので、この3つのreducerが想定通り機能しているかどうかをテストしています。
import reducer, {addTodo, deleteTodo, completeTodo} from '../features/todoSlice'
describe("Reducer test", ()=> {
it("addTodo", ()=> {
let initialState = {
count: 0,
todo: []
}
const action = {type: addTodo.type, payload: 'walk the dog'}
const state = reducer(initialState, action);
expect(state.todo).toEqual([{id: 0, text: 'walk the dog', completed: false}])
})
it("deleteTodo", ()=> {
let initialState = {
count: 0,
todo: [{id: 0, text: 'walk the dog', completed: false}]
}
const action = {type: deleteTodo.type, payload: 0}
const state = reducer(initialState, action);
expect(state.todo).toEqual([])
})
it("completeTodo", ()=> {
let initialState = {
count: 0,
todo: [{id: 0, text: 'walk the dog', completed: false}]
}
const action = {type: completeTodo.type, payload: 0}
const state = reducer(initialState, action);
expect(state.todo).toEqual([{id: 0, text: 'walk the dog', completed: true}])
})
})
次に、react-redux
周りのテストをします。
主な内容は、inputから入力したtodoがちゃんとReactのコンポーネント内にレンダリングされているか、delete
したらちゃんと画面上から消えるかなどをチェックします。
今回はReduxのstoreに保存したtodoがReact内でレンダリングされているかテストしているので、テストするコンポーネントの<Todo>
をテストファイル内で<Provider store={store}></Provider>
で囲んでいます。
このstoreはredux-mock-store
を使ってモックする方法もあるのですが、とりあえずスタンダード?なやり方でテストしてみました。
import { render, cleanup, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {Provider} from 'react-redux'
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from '../features/todoSlice'
import Todo from '../features/Todo'
afterEach(()=> cleanup());
describe("Render test", ()=> {
let store;
beforeEach(()=> {
store = configureStore({ reducer: rootReducer })
})
it("should render todo text into the list",()=> {
render(<Provider store={store}><Todo/></Provider>)
userEvent.type(screen.getByPlaceholderText("Enter"),"walk the dog")
userEvent.click(screen.getByText("Add Todo"))
expect(screen.getByTestId("test-todo")).toHaveTextContent("walk the dog")
})
it("should line-through todo if the todo been completed", ()=> {
render(<Provider store={store}><Todo/></Provider>)
userEvent.type(screen.getByPlaceholderText("Enter"),"walk the dog")
userEvent.click(screen.getByText("Add Todo"))
userEvent.click(screen.getByRole("checkbox"))
expect(screen.getByTestId("test-todo2")).not.toBeNull()
})
it("should be removed from the screen if been deleted", ()=> {
render(<Provider store={store}><Todo/></Provider>)
userEvent.type(screen.getByPlaceholderText("Enter"),"walk the dog")
userEvent.click(screen.getByText("Add Todo"))
userEvent.click(screen.getByText("×"))
expect(screen.getByRole("todoul")).not.toHaveTextContent("walk the dog")
})
})
メモがてら書いてみてみたのですが、テストのやり方はもうちょっと色々なやり方を知っておきたいなと思っているので、また追記するかもしれません。