Jest: How to mock redux-thunk api response?

1.5k Views Asked by At

I am having hard time to mock api response in my thunk. Mock returns data undefined.

Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: {"payload": {"data": ["hey", "foo"], "resultValue": 0}, "type": "products/setProducts"}

Received 1 Object { - "payload": Object { - "data": Array [ - "hey", - "foo", - ], - "resultValue": 0, + "meta": Object { + "arg": undefined, + "requestId": "xwidHSyGluO0zxXJgYNCs", + "requestStatus": "pending", }, - "type": "products/setProducts", + "payload": undefined, + "type": "products/getProducts/pending", }, 2 Object { - "payload": Object { - "data": Array [ - "hey", - "foo", - ], - "resultValue": 0, - }, - "type": "products/setProducts", + "payload": undefined, + "type": "products/setFetching", },

API file object with methods

import { instance } from "./index";
import { ProductType } from "../types/ProductType";

export const productsApi = {
    requestProducts() {
        return instance.get<ProductType[]>('products')
            .then(response => response.data)
    },
}

THUNK

export const getProducts = createAsyncThunk<void, void>
("products/getProducts",
    async function (_, { dispatch }) {
        dispatch(setFetching())
        try {
            const response = await productsApi.requestProducts() `Here is problem with mocking, returns undefined`
            dispatch(setProducts(response))
        } catch {
            dispatch(setError('Some error has been occurred'))
        } finally {
            dispatch(setFetching())
        }
    });

TEST

import productsSlice, {
    getProducts,
    productsSelector,
    ProductsStateType,
    setError,
    setFetching,
    setProducts
} from "./productsSlice";
import { RootState } from "../redux-store";
import { productsApi } from "../../api/products-api";

jest.mock("../../api/products-api")

const productsApiMock = productsApi as jest.Mocked<typeof productsApi>;

const dispatchMock = jest.fn();
const getStateMock = jest.fn();

beforeEach(() => {
    dispatchMock.mockClear();
    getStateMock.mockClear();
    productsApiMock.requestProducts.mockClear();
});

const result = {
    resultValue:0,
    data: ['hey','foo']
}

productsApiMock.requestProducts.mockReturnValueOnce(Promise.resolve(result));

describe("products reducer thunk", () => {
    it("getProducts thunk success ", async () => {
        const thunk = getProducts();
        thunk(dispatchMock, getStateMock, {});
        expect(dispatchMock).toBeCalledTimes(2);
        expect(dispatchMock).toHaveBeenCalledWith(setProducts(result));
    });
});
1

There are 1 best solutions below

0
On

I had the same problem. Moving the line with .mockReturnValueOnce inside test callback helps me.

describe("products reducer thunk", () => {
    it("getProducts thunk success ", async () => {
        //Like that
        productsApiMock.requestProducts.mockReturnValueOnce(Promise.resolve(result));
        const thunk = getProducts();
        thunk(dispatchMock, getStateMock, {});
        expect(dispatchMock).toBeCalledTimes(2);
        expect(dispatchMock).toHaveBeenCalledWith(setProducts(result));
    });
});