0

I have a custom hook, that calls a saga function that in turn calls an axios api, when testing this function im getting

TypeError: undefined is not a function

I just want to test if this function got called.

postsHook.test.tsx

import { renderHook } from "@testing-library/react-hooks";
import usePostsHook from "./postsHook";
import { initCommentUpdates, getPostsInit } from "../actions/postActions";
import { getPosts } from "../selectors/selectors";
import { useSelector, useDispatch } from "react-redux";

describe("usePostsHook hook", () => {
    const [posts] = renderHook(() => usePostsHook());
    expect(posts).toHaveBeenCalledWith(1);
});

postsHooks.tsx

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { initCommentUpdates, getPostsInit } from "../actions/postActions";
import { getPosts } from "../selectors/selectors";
function usePostsHooks() {
    const dispatch = useDispatch();
    const posts = useSelector(getPosts());
    React.useEffect(() => {
        dispatch(getPostsInit());
        console.log("post hooks got called");
        dispatch(initCommentUpdates());
    }, []);
    return [posts];
}

export default usePostsHooks;
3
  • You need to wrap your render with a redux store & mock the selector and the dispatch function Commented May 14, 2020 at 15:08
  • Can you demonstrate how i would be able to do this ? Commented May 14, 2020 at 15:09
  • 1
    Of course, posted a partial answer, tell me whenever I need to add or explain something else Commented May 14, 2020 at 15:14

1 Answer 1

2

posts is not a function in your hook, but something selected from store and I presume is the return from your API. so expect(posts).toHaveBeenCalledWith(1); is natural since it is not a function.

To test whether your dispatch occurred or not, you need to mock it. Here is an example I use in my tests:

import * as ReactRedux from 'react-redux';

// this mock will be the dispatch function that redux returns on useDispatch()
const mockDispatch = jest.fn();

beforeAll(() => {
  // tells useDispatch to return the mocked dispatch
  ReactRedux.useDispatch = jest.fn().mockImplementation(() => mockDispatch);
  // tells useSelector to return an empty array
  ReactRedux.useSelector = jest.fn().mockImplementation(() => []);
});


beforeEach(() => {
  // clear the mocks to refresh their calls info
  ReactRedux.useDispatch.mockClear();
  mockDispatch.mockClear();
});

Later, in your test

expect(posts).toEqual([])
expect(mockDispatch).toHaveBeenCalledWith(getPostsInit())

The thing here is that you are just unit testing your hook, ie: It returns what useSelector returns, it fires some dispatches, without knowing what is the actual real implementation of useSelector or useDispatch

Sign up to request clarification or add additional context in comments.

15 Comments

about to try this out, not sure why this got downvoted.
Let's wait for the person that downvoted both the question and the answer to show up :) I will edit to add the mock for the selector as well, because firing the actual useSelector will result in an error: cannot find redux store. But the technique is the same when mocking the dispatch
Im using typescript(tsx) and im getting Property 'mockClear' does not exist on type
@Inceptor I think i need to read more information on this, and figure this out, i appreciate the support.
This expect will assert that your dispatch was called with a getPostsInit() action
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.