我是 redux 工具包库的新手,尤其是在测试方面。我浏览了文档并阅读了一堆关于这个主题的帖子和文章,但仍然很挣扎。我构建了一个简单的待办事项应用程序,并包含了几个 API 请求来涵盖异步情况。不过,测试这些结果有点挑战性。我希望就我的代码以及可以改进的地方获得一些建议和反馈。我还想对测试 createAsyncThunk 切片是否有意义提出一些意见。注意:我对测试 API 调用本身不感兴趣,并使用模拟数据重新创建成功的请求。
建设性的批评非常有帮助,将不胜感激
请看一下我的切片文件并进行测试
postSlice.ts
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "../../store";
import axios from "axios";
export type Post = {
userId: number;
id: number;
title: string;
body: string;
};
export type PostsState = {
posts: Post[];
loading: boolean;
error: null | string;
};
export const initalPostState: PostsState = {
posts: [],
loading: false,
error: null,
};
export const fetchAllPosts = createAsyncThunk(
"posts/allPosts",
async (data, { rejectWithValue }) => {
try {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/posts`
);
return (await response.data) as Post[];
} catch (err) {
if (!err.response) {
throw err;
}
return rejectWithValue(err.response.data);
}
}
);
export const fetchSuccessful = fetchAllPosts.fulfilled;
export const fetchPending = fetchAllPosts.pending;
export const fetchFailed = fetchAllPosts.rejected;
const postsSlice = createSlice({
name: "Posts",
initialState: initalPostState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchSuccessful, (state, { payload }) => {
state.posts = payload;
state.loading = false;
});
builder.addCase(fetchPending, (state, action) => {
state.loading = true;
});
builder.addCase(fetchFailed, (state, action) => {
state.error = action.error.message
? action.error.message
: "Failed to load data";
state.loading = false;
});
},
});
export const selectPosts = (state: RootState) => state.fetchedPosts;
export const fetchedPostsReducer = postsSlice.reducer;
测试
postSlice.test.ts
import {
initalPostState,
fetchPending,
fetchFailed,
selectPosts,
fetchSuccessful,
fetchedPostsReducer,
} from "./postsSlice";
import { Post, PostsState } from "./postsSlice";
import store, { RootState } from "../../store";
const appState = store.getState();
describe("postsSlice", () => {
describe("Posts State, Posts Action and Selector", () => {
it("should set loading state on true when API call is pending", async (done) => {
// Arrange
// Act
const nextState: PostsState = await fetchedPostsReducer(
initalPostState,
fetchPending
);
// Assert
const rootState: RootState = { ...appState, fetchedPosts: nextState };
expect(selectPosts(rootState).loading).toBeTruthy();
expect(selectPosts(rootState).error).toBeNull();
done();
});
it("should set error state when API call is rejected", async (done) => {
// Arrange
const response = {
message: "Network request failed",
name: "error",
};
// Act
const nextState: PostsState = await fetchedPostsReducer(
initalPostState,
fetchFailed(response, "")
);
// Assert
const rootState: RootState = { ...appState, fetchedPosts: nextState };
expect(selectPosts(rootState).loading).toBeFalsy();
expect(selectPosts(rootState).error).not.toBeNull();
expect(selectPosts(rootState).error).toEqual("Network request failed");
done();
});
it("should update state when API call is successful", async (done) => {
// Arrange
const response: Post[] = [
{
userId: 1,
id: 1,
title:
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
body:
"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto",
},
{
userId: 1,
id: 2,
title: "qui est esse",
body:
"est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla",
},
];
// Act
const nextState: PostsState = await fetchedPostsReducer(
initalPostState,
fetchSuccessful(response, "")
);
// Assert
const rootState: RootState = { ...appState, fetchedPosts: nextState };
expect(selectPosts(rootState).loading).toBeFalsy();
expect(selectPosts(rootState).error).toBeNull();
expect(selectPosts(rootState).posts).toEqual(
expect.arrayContaining(response)
);
done();
});
});
});