Apollo MockedProvider not returning expected data
Asked Answered
W

2

5

I wrote a hook that calls apollo useQuery. It's pretty simple:

useDecider:

import { useState } from 'react';
import { useQuery, gql } from '@apollo/client';

export const GET_DECIDER = gql`
  query GetDecider($name: [String]!) {
    deciders(names: $name) {
      decision
      name
      value
    }
  }
`;

export const useDecider = name => {
  const [enabled, setEnabled] = useState(false);

  useQuery(GET_DECIDER, {
    variables: {
      name
    },
    onCompleted: data => {
      const decision = data?.deciders[0]?.decision;
      setEnabled(decision);
    },

    onError: error => {
      return error;
    }
  });

  return {
    enabled
  };
};

I'm trying to test it now and the MockedProvider is not returning the expected data:

import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { MockedProvider } from '@apollo/client/testing';
import { useDecider, GET_DECIDER } from './useDecider';

const getMock = (value = false, decider = '') => [
  {
    request: {
      query: GET_DECIDER,
      variables: {
        name: decider
      }
    },
    result: () => {
      console.log('APOLLO RESULT');

      return {
        data: {
          deciders: [
            {
              decision: value,
              name: decider,
              value: 10
            }
          ]
        }
      };
    }
  }
];

const FakeComponent = ({ decider }) => {
  const { enabled } = useDecider(decider);
  return <div>{enabled ? 'isEnabled' : 'isDisabled'}</div>;
};

const WrappedComponent = ({ decider, value }) => (
  <MockedProvider mocks={getMock(value, decider)} addTypename={false}>
    <FakeComponent decider={decider} />
  </MockedProvider>
);

describe('useDecider', () => {
  it('when decider returns true', () => {
    // should return true
    render(<WrappedComponent decider="fake_decider" value={true} />);
    screen.debug();
    const result = screen.getByText('isEnabled');
    expect(result).toBeInTheDocument();
  });
});
Wozniak answered 3/11, 2020 at 19:52 Comment(5)
Could you provide more information about what the MockedProvider is returning? Or perhaps a codesandbox?Rhea
MockedProvider is an export from @apollo: import { MockedProvider } from '@apollo/client/testing'; I'm trying to follow docs apollographql.com/docs/react/development-testing/testingWozniak
I meant, what data is the MockedProvider returning in your test? You said the data was not what was expected.Rhea
Nothing, data = undefined. I added console.log(loading, data) and loading = true` but i never see it switch to falseWozniak
I strongly recommend using jest.mockImplementation() for mocking Apollo queries' responses. MockedProvider works in a very mysterious wayRennold
M
5

From https://www.apollographql.com/docs/react/development-testing/testing/#testing-the-success-state

To test how your component is rendered after its query completes, you can await a zero-millisecond timeout before performing your checks. This delays the checks until the next "tick" of the event loop, which gives MockedProvider an opportunity to populate the mocked result

try adding before your expect call

  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });
Mallen answered 9/7, 2021 at 22:8 Comment(0)
R
3

I simplified your hook implementation and put together a working example:

import { useQuery, gql } from "@apollo/client";

export const GET_DECIDER = gql`
  query GetDecider($name: [String]!) {
    deciders(names: $name) {
      decision
      name
      value
    }
  }
`;

export const useDecider = (name) => {
  const { data } = useQuery(GET_DECIDER, { variables: { name } });
  return { enabled: data?.deciders[0]?.decision || false };
};

Note that in the test I also updated your getBy to an await findBy:

describe("useDecider", () => {
  it("when decider returns true", async () => {
    // should return true
    render(<WrappedComponent decider="fake_decider" value={true} />);
    screen.debug();
    const result = await screen.findByText("isEnabled");
    expect(result).toBeInTheDocument();
  });
});

This is because you need to wait for your API call to complete before the data will be on the page, hence you would not expect the data to be there on the first render.

enter image description here

Rhea answered 3/11, 2020 at 20:15 Comment(7)
Tried your implementation but the test is failingWozniak
Did you update the test? The code is working on the link I posted.Rhea
if you console out the result from the test, it's not correct. Not sure why the test is passing.Wozniak
Not sure what you mean. I logged the result and got <div>isEnabled</div>. Am I missing something? Could you update the sandbox to show what isn't working?Rhea
After a restart it works in codesandbox, but I copied exactly the same test and updates to useDecider to local and it fails for some reasonWozniak
Can't do much more without new information. Let me know.Rhea
Yea there's an open issue github.com/apollographql/apollo-client/issues/6803 I had to use spyOn useQueryWozniak

© 2022 - 2024 — McMap. All rights reserved.