Vikash Saini
Vikash Saini

Reputation: 709

React Apollo Testing is not working. Query data is always coming undefined

I am getting loading state only and data as undefined in testing. I don't know why I am following everything in the given example. Please help.

Testing file. When i am waiting thsi line toexecute await wait(() => getByTestId('edit-category'));. It is giving response data of query as undefined. Error: TypeError: Cannot read property 'getCategory' of undefined Line 34 on editConatinerCategory.tsx => category={data!.getCategory!}

import React from 'react';
import gql from 'graphql-tag';
import { cleanup, wait } from 'react-testing-library';
import { customRender } from '../../../test-utils/customRender';
import { EditCategoryContainer } from './Container';

afterEach(() => {
  cleanup();
  console.error;
});
console.error = jest.fn();

const getCategoryMock = {
  request: {
    query: gql`
      query getCategory($id: Int!) {
        getCategory(id: $id) {
          id
          name
          active
          position
        }
      }
    `,
    variables: {
      id: 1
    }
  },
  result: {
    data: {
      getCategory: {
        id: 1,
        name: 'category',
        active: true,
        position: 1
      }
    }
  }
};

describe('create edit category module', () => {
  test('Rendering correct', async () => {
    const { container, debug, getByTestId } = customRender(<EditCategoryContainer />, [
      getCategoryMock
    ]);
    await wait(() => getByTestId('edit-category'));
    await wait(() => expect(container).toMatchSnapshot());
//Getting this TypeError: Cannot read property 'getCategory' of undefined. Because i am data as undefined from my query response

  });
});

CustomRender.tsx

import React from 'react';
import { render } from 'react-testing-library';
import { MockedProvider, MockedResponse } from 'react-apollo/test-utils';
import { Router, Switch } from 'react-router-dom';
import { createMemoryHistory } from 'history';

export const customRender = (
  node: JSX.Element | null,
  mocks?: MockedResponse[],
  {
    route = '/',
    history = createMemoryHistory({ initialEntries: [route] })
  } = {}
) => {
  return {
    history,
    ...render(
      <MockedProvider mocks={mocks} addTypename={false}>
        <Router history={history}>
          <Switch>{node}</Switch>
        </Router>
      </MockedProvider>
    )
  };
};

EditCategoryContainer.tsx

import React from 'react';
import { withRouter } from 'react-router';
import { Spin } from 'antd';
import {
  AddCategoryComponent,
  GetCategoryComponent
} from '../../../generated/graphql';
import { EditCategory } from './Edit';
import { LoadingComponent } from '../../../components/LoadingComponent';

export const EditCategoryContainer = withRouter(({ history, match }) => {
  const id: number = parseInt(match.params.id, 10);
  return (
    <GetCategoryComponent
      variables={{
        id
      }}
    >
      {({ data, loading: getCategoryLoading }) => {
        console.log(getCategoryLoading, 'getCategoryLoading');
        if (getCategoryLoading) {
          return <LoadingComponent />;
        }
        if (data && !data.getCategory) {
          return <div>Category not found!</div>;
        }
        console.log(data);
        return (
          <AddCategoryComponent>
            {(addCategory, { loading, error }) => {
              return (
                <EditCategory
                  data-testid="edit-category"
                  category={data!.getCategory!}
                  loading={loading || getCategoryLoading}
                  onSubmit={values => {
                    addCategory({ variables: values }).then(() => {
                      history.push('/dashboard/categories');
                    });
                  }}
                />
              );
            }}
          </AddCategoryComponent>
        );
      }}
    </GetCategoryComponent>
  );
});

Edit: I tried @mikaelrs solution which is passed match. But it is not working. I also tried to pass id:1 as fixed. But it is still giving error.

 <GetCategoryComponent
      variables={{
        id:1
      }}
    >
...rest of code.
</GetCategoryComponent>

This is not working. My query without veriable is working fine. Mutation is also working fine. I am having only problem with this. When i have to pass like varible like this.

Upvotes: 3

Views: 9114

Answers (2)

srk
srk

Reputation: 1901

I faced a similar issue. Here is how I resolved my issue.

First, wait for the query to resolve, as recommended by @mikaelrs and the docs:

await new Promise(resolve => setTimeout(resolve, 0));

After doing that, the loading property was false, but data was still undefined. I discovered that my mock result object was missing a property. Once I added that missing property to the mock result, the data was populated as expected.

Upvotes: 4

mikaelrs
mikaelrs

Reputation: 335

What I do to wait for the loading state of the MockedProvider to pass is to use the wait function from waait. This is actually what Apollo recommends as well.

So in your test you would do:

import React from 'react';
import gql from 'graphql-tag';
import { cleanup } from 'react-testing-library';
import wait from 'waait'

import { customRender } from '../../../test-utils/customRender';
import { EditCategoryContainer } from './Container';

afterEach(() => {
  cleanup();
});

const getCategoryMock = {
  request: {
    query: gql`
      query getCategory($id: Int!) {
        getCategory(id: $id) {
          id
          name
          active
          position
        }
      }
    `,
    variables: {
      id: 1
    }
  },
  result: {
    data: {
      getCategory: {
        id: 1,
        name: 'category',
        active: true,
        position: 1
      }
    }
  }
};

describe('create edit category module', () => {
  test('Rendering correct', async () => {
    const { container, debug } = customRender(<EditCategoryContainer />, [
      getCategoryMock
    ]);

    await wait(0);

    // Your loading state should be false after this, and your component should
    // get it's data from apollo for you to do any assertion you would like to 
    // after this point. To see that the component is rendered with data invoke 
    // the debug function from react-testing-library after this point


    debug();


    expect(container).toMatchSnapshot()
  });
});

Another solution is to use react-testing-librarys wait function to wait for an element that would be present after the loading state switches to true.

For instance

describe('create edit category module', () => {
  test('Rendering correct', async () => {
    const { container, debug, queryByText } = customRender(<EditCategoryContainer />, [
      getCategoryMock
    ]);

    await wait(()=> queryByText("Some Data"));

    // Your loading state should be false after this, and your component should
    // get it's data from apollo for you to do any assertion you would like to 
    // after this point

    expect(container).toMatchSnapshot()
  });
});

Upvotes: 6

Related Questions