5

so I am trying to test that the onSubmit function is getting triggered if the button is clicked - the way im doing this is through testing the internals of the onSubmit function is getting calles (axios post method)

the test

describe('RecipeSearch', () => {
    test('submit button should return post function to recipes/search/', () => {
        let mock = new MockAdapter(axios);
        userEvent.selectOptions(screen.getByRole('combobox'), 'Sweet');
        userEvent.click(screen.getByText('Search'));

        const config = {
            headers: {
                'Content-Type': 'application/json',
            },
        };
        const searchRecipes = mock.onPost(
            `${process.env.REACT_APP_API_URL}/recipes/search/`,
            { flavor_type: 'Sweet' },
            { config }
        );
        expect(searchRecipes).toHaveBeenCalled();
    });
});

the Error

    expect(received).toHaveBeenCalled()

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

    Received has type:  object
    Received has value: {"abortRequest": [Function abortRequest], "abortRequestOnce": [Function abortRequestOnce], "networkError": [Function networkError], "networkErrorOnce": [Function networkErrorOnce], "passThrough": [Function passThrough], "reply": [Function reply], "replyOnce": [Function replyOnce], "timeout": [Function timeout], "timeoutOnce": [Function timeoutOnce]}

the function

const recipeSearch = (props) => {
    const [formData, setFormData] = useState({
        flavor_type: 'Sour',
    });

    const { flavor_type } = formData;

    const [loading, setLoading] = useState(false);

    const onChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });

    const onSubmit = (e) => {
        e.preventDefault();

        const config = {
            headers: {
                'Content-Type': 'application/json',
            },
        };

        setLoading(true);
        axios
            .post(
                `${process.env.REACT_APP_API_URL}/recipes/search/`,
                {
                    flavor_type,
                },
                config
            )
            .then((res) => {
                setLoading(false);
                props.setRecipes(res.data);
                window.scrollTo(0, 0);
            })
            .catch((err) => {
                setLoading(false);
                window.scrollTo(0, 0);
            });
    };

    return (
        <form  onSubmit={(e) => onSubmit(e)}>
            <div>
                <div>
                    <div>
                        <label htmlFor='flavor_type'>Choose Flavor</label>
                        <select
                            name='flavor_type'
                            onChange={(e) => onChange(e)}
                            value={flavor_type}
                        >
                            <option value='Sour'>Sour</option>
                            <option>Sweet</option>
                            <option>Salty</option>
                        </select>
                    </div>

                    <div>
                            <button type='submit'>Search</button> 
                    </div>
                </div>
            </div>
        </form>
    );
};

i have added the whole test and the component code so helping would be easier. thanks in advance

(added the onChange + onSubmit functions)

EliyaMelamed
  • 276
  • 2
  • 8
  • 22

2 Answers2

4

Creating an onSubmit mock and passing it as a prop won't work since the onSubmit callback is internal to the component and not a prop - you don't have access to it from the test.

Rather than testing if the onSubmit has been called, you should test the result of triggering the submit event. Which in this case could mean verifying that the axios request is made.

See How do I test axios in Jest? for examples on how to mock axios in your test.

juliomalves
  • 42,130
  • 20
  • 150
  • 146
  • Thanks you helped a lot now I understand why the test keep failing ! But isn't there's a way that I can mock internal functions to use in tests? or should I just manually import the functions to the test? – EliyaMelamed Feb 25 '21 at 07:25
  • 1
    There isn't a way to mock internal functions. You could import it from an external file and mock the imported function during the test if you really wanted. – juliomalves Feb 25 '21 at 08:36
  • 1
    That being said, I wouldn't recommend changing your code just for testing purposes. As I said, you should test the result of triggering the `onSubmit` action instead. – juliomalves Feb 25 '21 at 08:38
  • Thanks man you helped a ton. I will give it a shot – EliyaMelamed Feb 26 '21 at 04:27
  • so I tried to test the result of the onSubmit function but still the test fails. – EliyaMelamed Mar 08 '21 at 18:29
  • You're not mocking `axios` properly in that test. Check [How do I test axios in Jest?](https://stackoverflow.com/questions/45016033/how-do-i-test-axios-in-jest) for possible solutions to that. – juliomalves Mar 17 '21 at 17:51
  • so I tried to mock the axios but still with no success, I have updated the test and the error in question. i would really appreciate if you could guide me how you would write the test. – EliyaMelamed Mar 19 '21 at 13:10
1

Did you try selecting the button by text :

 describe('RecipeSearch', () => {
    test('test clicking the button triggers the onSubmit function', () => {
        const onSubmit = jest.fn();
        render(<RecipeSearch onSubmit={onSubmit} />);
        userEvent.selectOptions(screen.getByRole('combobox'), 'Sour');
        userEvent.click(screen.getByText('Search'));
        expect(onSubmit).toHaveBeenCalled();
    });
});

I'm not sure how getByRole handles a second argument in your first try, but getByText should work.

Florian Motteau
  • 3,467
  • 1
  • 22
  • 42