1

I am using AWS co, Amplify and React I want to mock API.graphql imported from AWS. This is my helper function for asynchronous functions useAsync.ts

import { ErrorHandle } from '@app/components/ErrorHandle';
import { useCallback, useState } from 'react';

const getRetryWaitTime = (retryCount: number): number => {
  const httpRetryMaxWaitTime = 10 * 60 * 1000;
  const suggestedRetryWaitTime = 1000 * 2 ** retryCount;
  const retryWaitTime = Math.min(httpRetryMaxWaitTime, suggestedRetryWaitTime);
  return retryWaitTime;
};

const delay = (ms: number) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

const retry = (retryFunc: () => Promise<any>, retryCount = -1): Promise<any> => {
  return retryFunc().catch((err: any) => {
    if (err && retryCount) {
      retryCount -= 1;
      const waitTime = getRetryWaitTime(retryCount);
      return delay(waitTime).then(() => retry(retryFunc, retryCount));
    }
    throw err;
  });
};

export const useAsync = (
  asyncFunction,
  options: {
    onError?: (error, response) => any;
    onSuccess?: (data) => any;
    retryCount?: number;
  } = {},
) => {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  options.retryCount = options.retryCount || 1;
  options.onError = options.onError || ((error, response) => ErrorHandle(error, response));

  const execute = useCallback(
    async (payload?: any) => {
      setLoading(true);
      setData(null);
      setError(null);

      return retry(() => asyncFunction(payload), options.retryCount)
        .then((response) => {
          setData(response);
          if (options.onSuccess) {
            options.onSuccess(response);
          }
          setLoading(false);
          return { ok: true, data: response.data, response };
        })
        .catch((error: any) => {
          setError(error);
          if (options.onError) {
            options.onError(error, error ? error.response : null);
          }
          setLoading(false);
          return { error, ok: false, response: error, data: error };
        });
    },
    [asyncFunction, options],
  );

  return { execute, loading, data, error };
};

This is an example for my graphql mutations file. mutations.ts

export const createLanguage = /* GraphQL */ `
  mutation CreateLanguage($input: CreateLanguageInput!, $condition: ModelLanguageConditionInput) {
    createLanguage(input: $input, condition: $condition) {
      id
      name
      code
      createdAt
      updatedAt
      createdBy
      updatedBy
      direction
      owner
    }
  }
`;

This is actual code for mocking and unit testing my useLanguages file useLanguages.ts

import { createLanguage } from '@app/graphql/mutations';
import { API, graphqlOperation } from 'aws-amplify';
const useLanguages = () => {

  const [state, dispatch] = useStore();
  const languageAdd = useAsync(
   (value) => {
     const input: any = value;
     input.createdBy = state.user.attributes.email;
     input.updatedBy = state.user.attributes.email;
     return API.graphql(graphqlOperation(createLanguage, { input }));
   },
   {
     onSuccess: (result) => dispatch({ type: Actions.ADD_LANGUAGE, payload: result.data.createLanguage }),
   },
  );
  return { languageAdd };
}

I tried multiple variants this that and read a lot of documentations and articles like this that

Otabek Butcher
  • 545
  • 8
  • 19

1 Answers1

1

I ran straight into this, my app (NextJS) uses Amplify and was crashing with "Amplify not correctly configured" and so on in the unit tests.

Mocked it like this:

jest.mock('aws-amplify');

describe('Index', () => {
  beforeEach(() => {
    Amplify.configure({
      Auth: {
        identityPoolId: 'abc',
        region: 'def',
        userPoolId: 'ghi',
        userPoolWebClientId: 'jkl',
      },
    });
  });

This allowed the test to pass. Note that you could use this without the jest.mock line, but then Amplify validation will kick in and if your arguments don't pass, Amplify will throw all kinds of errors. Mocking the module this way skips that validation.

I'd assume whatever else you were trying to mock could be handled this way by adding the functions you need to the jest.mock line per the usual module mock overrides.

Tim Consolazio
  • 4,802
  • 2
  • 19
  • 28