2

I'm trying to use jest to write tests for when i use fetch to get and post data from a react page.

the current test is something like this - I'm confused about how to make sure the correct data is passed through the specific component.

The test itself is looking like this at the moment,

import React from 'react';
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';

import getSettings from 'components/onboarding/stages/useGetSettingsData.js'
import updateSettings from 'components/onboarding/stages/useUpdateSettings.js'
import Settings from 'components/onboarding/stages/useUpdateSettings.js'


describe('get settings', () => {
    it('should load settings with no issues', () => {
       getSettings().mockResolvedValueOnce({
        power: 'magical',
        potion: 'magilin',
        potion_times: '[]',
        potion_activity_times: '',
        gender: 'Male',
        age: '30',
      });
      const {getByText, getByLabelText,debug} = render( <Settings/> )
  
      expect(heading).toBeInTheDocument();

      expect(GetSettings)
    });
  });

Testing this get user data function,

import { useEffect, useState } from 'react';

export default function getSettings(auth) {
  const [settings, setSettings] = useState(false);

  useEffect(() => {
    try {
      const idToken = auth.getIdToken();

      const response = fetch('/api/dashboard/get-settings', {
        method: 'GET',
        headers: new Headers({
          Authorization: `Bearer ${idToken}`,
          'Content-Type': 'application/x-www-form-urlencoded',
        }),
      });
      const jsonReponse = response.json();

      if (!jsonReponse.error) {
        setSettings(jsonReponse.settings);
      } else {
        setSettings({
          power: 'magical',
        potion: 'magilin',
        potion_times: '[]',
        potion_activity_times: '',
        gender: 'Male',
        age: '30',
        });
      }
    } catch (error) {
      console.log('Error happened in decrypting token', error);
    }
  }, [settings]);
  return settings;
};

and this for setting settings,

import { loadingReducer } from 'components/onboarding/stages/loadingReducer';

export default function updateSettings({ newSettings, auth, initialState }) {
  const [state, dispatch] = loadingReducer(loadingReducer, initialState);

  dispatch({ type: 'LOADING' });
  try {
    const idToken = auth.getIdToken();

    const response = fetch('/api/dashboard/set-settings', {
      method: 'POST',
      headers: new Headers({
        Authorization: `Bearer ${idToken}`,
        'Content-Type': 'application/json',
      }),
      body: JSON.stringify(newSettings),
    });

    // const jsonReponse = response.json();

    // TODO: Create Notification Toast
  } catch (error) {
    console.log('Error happened in decrypting token', error);
  }
  dispatch({ type: 'FINISHED' });
  return { isLoading: state.isLoading };
}

Into a component like this,

function SettingsDetails() {
  const auth = useAuth();
  const { register, handleSubmit, errors } = useForm();
  const initialState = {
    isLoading: false,
  };
  // const settings = false
  const settings = getSettings(auth);

  const onSubmit = (data) => {
    console.log('Submitting..');
    const isLoading =updateSettings(data,initialState);
    return isLoading
  };  
 
  
  return (

    <div className="md:grid md:grid-cols-3 md:gap-6">
  <div className="md:col-span-1">
    <h3 className="text-lg font-medium leading-6 text-gray-900">
      Preferences
    </h3>
    <p className="mt-1 text-sm leading-5 text-gray-500">
      Set your Personal Preferences
    </p>
  </div>
  <div className="mt-5 md:mt-0 md:col-span-2">
    <form onSubmit={handleSubmit(onSubmit)}>
      <Dropdown
        data-testid = "power"
        registerInput={register}
        optionLabel="power"
        selectedOption={settings.power}
        optionName="power"
        allOptions={['ice', 'fire', 'Both']}
      />
.....
  <button
            className="inline-flex items-center justify-center px-5 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:text-indigo-600 hover:bg-indigo-50 focus:outline-none focus:shadow-outline focus:border-indigo-300 transition duration-150 ease-in-out"
            // variant={props.buttonColor}
            // size={props.inputSize}
            type="submit"
            disabled={initialState.isLoading}
          >
            {initialState.isLoading ? (
              <>
                <span>Saving</span>
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  viewBox="0 0 24 24"
                  className="animate-spin h-4 w-4 ml-3 fill-current"
                >
                  <path d="M0 11c.511-6.158 5.685-11 12-11s11.489 4.842 12 11h-2.009c-.506-5.046-4.793-9-9.991-9s-9.485 3.954-9.991 9h-2.009zm21.991 2c-.506 5.046-4.793 9-9.991 9s-9.485-3.954-9.991-9h-2.009c.511 6.158 5.685 11 12 11s11.489-4.842 12-11h-2.009z" />
                </svg>
              </>
            ) : (
              <span>Save</span>
            )}
          </button>

I'm getting a bit lost on the best approach to try and mock this get data functionality - any best practices?

I just want to make sure that the page renders, that it renders the data from the getdata function, and that it saves data on submission to the form properly. Really hard to find any resources online that sort of go through this.

Currently I get this error when running the test,

   Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
    1. You might have mismatching versions of React and the renderer (such as React DOM)
    2. You might be breaking the Rules of Hooks
    3. You might have more than one copy of React in the same app
    See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

Thanks in advance,

update to current test with new error (thanks for help so far!). The test for mocking those functions work now! I would like to be able to mock the functions themselves to ensure they are working as well though.

the loading reducer for updating settings is this btw,

export default function loadingReducer(state, action) {
  switch (action.type) {
    case 'LOADING':
      return { isLoading: true };
    case 'FINISHED':
      return { isLoading: false };
    default:
      return state;
  }
}
1
  • 1
    You're not mocking the getSettings hook properly (calling getSettings() is what's causing the Invalid hook call error). I suggest having a look at Mocking React custom hook with Jest. Commented Jun 27, 2021 at 14:00

1 Answer 1

1

What I would try is instead of importing the getSettings in my test, just mocking the whole file and the hook. So instead of

import getSettings from 'components/onboarding/stages/useGetSettingsData.js'

do something like

jest.mock('components/onboarding/stages/useGetSettingsData.js', () => {
      return {
          getSettings: jest.fn(() => {
            return {
              power: 'magical',
              potion: 'magilin',
              potion_times: '[]',
              potion_activity_times: '',
              gender: 'Male',
              age: '30',
             };
            });
          };
        });
    }

In that way you just mock the whole useGetSettingsData.js file and you can test your component changes.

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

6 Comments

Thanks this is a good point. I do want to make sure I am also successfully mocking the getSettings functionality as well though - make sure I'm not leaving any issues around. Is it possible there is a simple way to do both components? (maybe there is a way of mocking fetches?
And how would I load this mock into the existing component?
By mocking the whole file you actually say to jest to use your mock instead of your actual code. So jest will not import useGetSettingsData.js but will try to run your component test by using the mock. So in that sense you don't have to worry about mocking fetch or any other methods inside your getSettings, unless you want to mock them for a specific test.
you don't have to load it jest will pick it up automatically.
The above code i tried is failling atm @SakisTsalk
|

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.