value must be a mock or spy function when using jest.fn
Asked Answered
C

3

16

Getting this error

Matcher error: received value must be a mock or spy function

Received has type:  object
Received has value: {}

However, i think i shouldn't be getting this error because im using jest.fn. So im mocking the function.

describe('Should simulate button click', ()=> {
        it('should simulate button click', () => {
            // add the name of the prop, which in this case ites called onItemAdded prop,
            // then use jest.fn()
            const wrapper = shallow(<TodoAddItem onItemAdded={() => jest.fn()}/>)
            // console.log('props',wrapper.find('button').props());
            wrapper.find('button').simulate('click');

            expect(wrapper).toHaveBeenCalled(); // error happens when this executes
        })
    })

todo-add-item.js

import React, { Component } from 'react';

import './todo-add-item.css';

export default class TodoAddItem extends Component {

    render() {
        return (
            <div className="todo-add-item">

                <button 
                    className="test-button btn btn-outline-secondary float-left"
                    onClick={() => this.props.onItemAdded('Hello world')}>
                    Add Item
                </button>
            </div>
        );
    }
}

app.js (using the component in this file)

import React, { Component } from 'react';

import AppHeader from '../app-header';
import SearchPanel from '../search-panel';
import TodoList from '../todo-list';
import ItemStatusFilter from '../item-status-filter';
import TodoAddItem from '../todo-add-item';

import './app.css';

export default class App extends Component {

    constructor() {
        super();

        this.createTodoItem = (label) => {
            return {
                label,
                important: false,
                done: false,
                id: this.maxId++
            }
        };

        this.maxId = 100;

        this.state = {
            todoData: [
                this.createTodoItem('Drink Coffee'),
                this.createTodoItem('Make Awesome App'),
                this.createTodoItem('Have a lunch')
            ]
        };

        this.deleteItem = (id) => {
            this.setState(({ todoData }) => {
                const idx = todoData.findIndex((el) => el.id === id);
                const newArray = [
                    ...todoData.slice(0, idx),
                    ...todoData.slice(idx + 1)
                ];

                return {
                    todoData: newArray
                };
            });
        };

        this.addItem = (text) => {
            const newItem = this.createTodoItem(text);

            this.setState(({ todoData }) => {
                const newArray = [
                    ...todoData,
                    newItem
                ];

                return {
                    todoData: newArray
                };
            });
        };

        this.onToggleImportant = (id) => {
            console.log('toggle important', id);
        };

        this.onToggleDone = (id) => {
            console.log('toggle done', id);
        };
    };

    render() {
        return (
            <div className="todo-app">
                <AppHeader toDo={ 1 } done={ 3 } />
                <div className="top-panel d-flex">
                    <SearchPanel />
                    <ItemStatusFilter />
                </div>
                <TodoList
                    todos={ this.state.todoData }
                    onDeleted={ this.deleteItem }
                    onToggleImportant={ this.onToggleImportant }
                    onToggleDone={ this.onToggleDone } />
                <TodoAddItem onItemAdded={ this.addItem } />
            </div>
        );
    };
};
Cornstarch answered 15/5, 2019 at 18:12 Comment(0)
W
9

I'm not 100% sure, but I believe you should do something like this:

describe('should simulate button click', () => {
   it('should simulate button click', () => {
      const mockedFunction = jest.fn();

      const wrapper = shallow(<TodoAddItem onItemAdded={ mockedFunction } />);

      wrapper.find('button').simulate('click');

      expect(mockedFunction).toHaveBeenCalled();
   });
});

You are testing if the onItemAdded function gets called when you click the <TodoAddItem /> component. So you have to mock it first using jest.fn and then check if the mocked function got called after you simulated the click.

Winchester answered 15/5, 2019 at 18:56 Comment(2)
it passed, awesome. Why do you think your solution worked ?Cornstarch
@Cornstarch it works because wrapper is an object, not the mock function itself. When passing a reference to the mockedFunction to wrapper, you can use that same mockedFunction constant to reference to jest.fn(). In this case both will point to jest.fn() therefore you can check if it has been called.Tarkany
R
7

For me works replacing the next one:

const setCategories = () => jest.fn();

With this one:

const setCategories = jest.fn();

I suppose that you should to set just jest.fn or jest.fn() in your code.

Ripplet answered 15/9, 2020 at 6:8 Comment(0)
S
0

Found a more summarized explanation of jest mocks and SpyOn functions here: https://codeyourgreens.com/jest/mock-functions/ . Could help guide towards implementing suitably to your needs.

Sewoll answered 2/7, 2023 at 19:36 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Yves

© 2022 - 2024 — McMap. All rights reserved.