dimanche 7 juin 2020

Testing createAsyncThunk Redux Toolkit Jest

I am newbee with the redux toolkit library and especially when it comes to testing. I looked through the documentation and read a bunch of posts and articles in regards to this subject but still struggle. I build a simple todo app and included a couple of API requests to cover asynchronous cases. Testing those turned out to be a bit challenging though. I am hoping to get some advice and feedback on my code and what could be improved. I also wanted some opinions on whether testing the createAsyncThunk slice makes sense or not. NOTE: I am not interested in testing the API calls itself, and use mock data to recreate a successful request.

Constructive criticism is very helpful and would be highly appreciated

Please take a look of one of my slice file and test

postsSlice.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;

Testing

postsSlice.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();
    });
  });
});

Aucun commentaire:

Enregistrer un commentaire