react-apollo icon indicating copy to clipboard operation
react-apollo copied to clipboard

writeQuery doesn't work in unit tests on useMutation

Open muhraff opened this issue 4 years ago • 5 comments

Intended outcome:

I have a mutation with an update option which calls cache.writeQuery.

This is updating the cache and re-rendering queries as expected when we run in the browser.

In unit tests for that component, the cache is not updating when cache.writeQuery is called.

Container.js

const customerAddressesQuery = gql`...`
const customerAddressDelete = gql`...`

const Container = () => {
 const [deleteAddress] = useMutation(customerAddressDelete, {
    update: (cache, { data }) => {
      const { deletedAddressId } = data.customerAddressDelete
      const { customer } = cache.readQuery({ query: customerAddressesQuery })

      cache.writeQuery({
        query: customerAddressesQuery,
        data: {
          customer: {
            ...customer,
            addresses: {
              ...customer.addresses,
              edges: customer.addresses.edges.filter(
                item => item.node.id !== deletedAddressId
              ),
            },
          },
        },
      })
    },
  })

  return <Component deleteAddress={deleteAddress} />
})

Container.test.js

import { render, waitForElement, waitForElementToBeRemoved, fireEvent  } from '@testing-library/react'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { MockedProvider } from '@apollo/react-testing'
import Container from '../Container'

test('should able to delete the address', async () => {
    const MOCKS = [
      {
        request: {
          query: customerAddressesQuery,
        },
        result: {
          data: {
            customer: {
              id: 'customer_id_0001',
              addresses: {
                edges: [
                  {
                    node: {
                      company: 'company name',
                      id: 'address_id_0001',
                      name: 'Name A',
                      phone: '+0000000000',
                      formatted: ['address', 'apartment', 'city', 'country'],
                      __typename: 'MailingAddress',
                    },
                    __typename: 'MailingAddressEdge',
                  },
                ],
                __typename: 'MailingAddressConnection',
              },
              defaultAddress: {
                id: 'address_id_0001',
                __typename: 'MailingAddress',
              },
              __typename: 'Customer',
            },
          },
        },
      },
      {
        request: {
          query: customerAddressDelete,
          variables: {
            addressId: 'address_id_0001',
          },
        },
        result: {
          data: {
            customerAddressDelete: {
              customerUserErrors: [],
              deletedCustomerAddressId: 'address_id_0001',
              __typename: 'CustomerAddressDeletePayload',
            },
          },
        },
      },
    ]

    const cache = new InMemoryCache()

    const { getByTestId, getByText } = render(
      <MockedProvider cache={cache} mocks={MOCKS}>
        <Container />
      </MockedProvider>
    )

    const deleteButton = await waitForElement(() => getByTestId('delete-button'))

    fireEvent.click(deleteButton)

    await waitForElementToBeRemoved(() => getByText('Name A'))
   // <------ Test fails here because element is not removed

    expect(getByText('Name A')).toBeNull()
  })

Actual outcome:

How to reproduce the issue:

Log the entire cache to the console before and after writeQuery is called. Value never changes.

Version

@testing-library/[email protected]
@apollo/[email protected]
[email protected]
@apollo/[email protected]

muhraff avatar Nov 14 '19 03:11 muhraff

@muhraff hi, did you fix this?

korgara avatar Dec 20 '19 23:12 korgara

@korgara Unfortunately No.

muhraff avatar Dec 22 '19 21:12 muhraff

In my case, as I inspect cache in the debugger, readQuery and writeQuery are not defined at all. I'm trying to mock them out in my tests, without success so far.

dmt0 avatar May 12 '20 17:05 dmt0

@dmt0 Thank you for the clue. I can solve my problem with this code

I'm using react-testing-library

const renderWithProvider = async (mocks, cache) => 
  <MockedProvider {...{ mocks }} cache={cache} addTypename={false}>
    <MyComponent />
  </MockedProvider>

it("example test case.", async () => {
  const cache = new InMemoryCache({ addTypename: false });
  const utils = await renderWithProvider(cache);

  // Doing call mutation with writeQuery
  // Don't forget to check the mutation is done

  const afterMutationResult = cache.readQuery({
    query: {WRITED_QUERY_HERE}
  });

  // then, the 'afterMutationResult' has data after writeQuery
  expect(afterMutationResult).toStrictEqual(whatYouExpecting);
});

HTMLhead avatar Jun 17 '20 11:06 HTMLhead

I ended up solving this like so:

export const mockCache = () => {
  const map = new Map();
  return {
    size: () => map.size,
    readQuery: ({query, variables}) =>
      map.get(query + JSON.stringify(variables)),
    writeQuery: ({query, variables, data}) =>
      map.set(query + JSON.stringify(variables), data),
  };
};

const cache = new InMemoryCache();
Object.assign(cache, mockCache());

dmt0 avatar Jun 17 '20 12:06 dmt0