Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MockedProvider does not return mocks in a storybook. #7081

Open
MarkLyck opened this issue Sep 27, 2020 · 39 comments
Open

MockedProvider does not return mocks in a storybook. #7081

MarkLyck opened this issue Sep 27, 2020 · 39 comments

Comments

@MarkLyck
Copy link

MarkLyck commented Sep 27, 2020

Intended outcome:
The MockedProvider should work in a storybook environment and return the mock request provided to the MockProvider as per the docs: https://www.apollographql.com/docs/react/api/react/testing/#mockedprovider

Actual outcome:
When running a story with MockedProvider, the useQuery hook returns:

{
  error: undefined,
  loading: true,
  data: undefined
}

then it returns:

{
  error: undefined,
  loading: false,
  data: undefined
}

It does not return the mock I provided in my story

How to reproduce the issue:

Here is my story.tsx file

import React from 'react'
import { MockedProvider } from "@apollo/client/testing";
import LatestSells from '~/components/SalesPage/LatestSells'
import { LATEST_SELL_SIGNALS } from '~/common/queries'

export default {
  title: 'Sales page/latest sells',
}


const mocks = [
  {
    request: {
      query: LATEST_SELL_SIGNALS,
      variables: {},
    },
    result: {
      data: {
        dog: {
          name: "Douglas"
        }
      }
    }
  }
]

export const latest_sells = () => {
  return (
    <MockedProvider mocks={mocks} addTypename={false}>
      <LatestSells />
    </MockedProvider>
  )
}

I tried both with and without addTypename={false}

I made my component as simple as possible to troubleshoot, but it doesn't work even when simplified down to the smallest possible react component.

component:

import React from 'react'
import { useQuery } from '@apollo/client'
import { LATEST_SELL_SIGNALS } from '~/common/queries'

const LatestSells = () => {
  const { loading, error, data } = useQuery(LATEST_SELL_SIGNALS)
  console.log(loading, error, data)
  return null
}

export default LatestSells

Even with the simplest possible setup, this does not work.

Lastly here is my query:

import { gql } from 'apollo-boost'

export const LATEST_SELL_SIGNALS = gql`
  query {
    latestSellSignalsList(orderBy: createdAt_DESC, first: 10) {
      items {
        name
        ticker
        boughtAt
        soldAt
      }
    }
  }
`

I've doubled checked that the query console logs correctly and the same in both the story and the actual component.

Versions
System:
OS: macOS 10.15.6
Binaries:
Node: 14.4.0 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.5 - /usr/local/bin/npm
Browsers:
Chrome: 85.0.4183.121
Firefox: 81.0
Safari: 14.0
npmPackages:
@apollo/client: ^3.2.1 => 3.2.1
apollo-boost: ^0.4.9 => 0.4.9
apollo-link: ^1.2.14 => 1.2.14
apollo-link-batch-http: ^1.2.14 => 1.2.14
apollo-link-context: ^1.0.20 => 1.0.20
apollo-link-error: ^1.1.13 => 1.1.13
apollo-utilities: ^1.3.4 => 1.3.4

@MarkLyck MarkLyck changed the title MockedProvider not returning anything in a storybook. MockedProvider does not return mocks in a storybook. Sep 27, 2020
@benjamn
Copy link
Member

benjamn commented Sep 29, 2020

@MarkLyck Are you actually still using apollo-boost, apollo-link, apollo-link-batch-http, etc? For the link imports, HttpLink should be imported directly from @apollo/client, and the other links can be imported from @apollo/client/link/core, @apollo/client/link/batch-http, @apollo/client/link/context, and/or @apollo/client/link/error.

@benjamn
Copy link
Member

benjamn commented Sep 29, 2020

We even have a codemod to help automate the conversion of imports: https://github.com/apollographql/apollo-client/tree/main/scripts/codemods/ac2-to-ac3

@akd3vs
Copy link

akd3vs commented Sep 29, 2020

I have the same issue with @apollo/client/testing I have the mocks but the request data comes undefined.

@MarkLyck
Copy link
Author

MarkLyck commented Sep 29, 2020

I find this a strange user-behaviour, but I figured it out.

I was testing it with dummy data this entire time because I just wanted to see the data return "something".

However, apparently if you give the mockProvider a mock that doesn't 100% match the expected schema, it will just give up and return undefined with no error, warning or anything.

When I replaced my dummy data, with a data matching the exact schema of my request it works corrrectly.

Wasted a lot of time on this detail. Please consider adding an error or warning if the mock doesn't match the expected return, if this is the intended behaviour.

P.S.
I also tried setting my apollo-link and apollo-boost imports to @apollo-client, but that did not affect anything.

benjamn added a commit that referenced this issue Oct 1, 2020
See benjamn/wryware#21 for explanation and
motivation. Should help with #6771, #6803, #6688, #6378, and #7081, and
possibly other regressions that began in @apollo/[email protected].
benjamn added a commit that referenced this issue Oct 1, 2020
See benjamn/wryware#21 for explanation and
motivation. Should help with #6771, #6803, #6688, #6378, and #7081, and
possibly other regressions that began in @apollo/[email protected].
@benjamn
Copy link
Member

benjamn commented Oct 1, 2020

This behavior should be fixed/improved in @apollo/[email protected] (just published to npm), thanks to #7108. Please give it another try after updating!

@betofigueiredo
Copy link

Hi @benjamn.
I just tried with "@apollo/client": "^3.3.0-beta.9" but had the same problem @MarkLyck said before.
Got it working after setting the result data exactly like the schema of my request.

@JohannesKlauss
Copy link

JohannesKlauss commented Oct 25, 2020

@benjamn Can second that. This behavior is still the same with @apollo/[email protected].
It's actually worse, because apollo doesn't even throw an error anymore when a mock is not found/matched.

@tylerbecks
Copy link

I am having this issue right now and it's very difficult to debug what I need to do to return my data. If I change my mocks to use a function to return the result, I can see it's running.

{
    request: {
      query: GET_HOUSE_BY_ID,
      variables: {
        id: PROPERTY_ID,
      },
    },
    result: () => {
      console.log('This runs in my tests but data is undefined')
      return {
        data: {
          houses_by_pk: house,
        }
      }
    }
  },

@cgrime01
Copy link

cgrime01 commented Dec 1, 2020

Having the same issue. As a workaround you may set the fetchPolicy == "no-cache" in the useQuery options:

const { loading, error, data } = useQuery(LATEST_SELL_SIGNALS, {fetchPolicy: "no-cache"})

@lukaszkalemba
Copy link

Getting same issue with @apollo/[email protected] even if mocked data matches exactly query inside component.

@zeroliu
Copy link

zeroliu commented Dec 13, 2020

Also found that if the nested mock response does not include __typename, it will also be removed silently.

@martimalek
Copy link

martimalek commented Dec 18, 2020

I think this may be related.

Regarding addTypename behaviour.

With the following query:

const SIMPLE_QUERY = gql`
	{
		a {
			b
		}
	}
`;

and testing the following hook:

const useSimpleHook = () => {
	const { data, loading, error } = useQuery(SOME_QUERY);

	return { data, loading, error };
};

when addTypename is set to false the hook returns the error:

ApolloError: No more mocked responses for the query: {
    a {
      b
      __typename
    }
  }

I thought from this explanation in the docs that setting addTypename to false would make the __typename property not appear. Instead when setting addTypename to true the test passes successfully and the mock data is returned.

I should also say that the majority of issues during testing had to do with the cache not being reset on every test.

@ekremkenter
Copy link

ekremkenter commented Jan 3, 2021

Getting the same issue with @apollo/[email protected] and @apollo/[email protected] with installed the dependency of @wry/[email protected] 😞

@vtereshyn
Copy link

sorry, any updates here?

@JeffDemanche
Copy link

I'm also running into issues getting MockedProvider to do anything. I've looked at the queries for some time and it's always just returning undefined for data with no logging as to what might be wrong with my query. This is with @apollo/client ^3.2.5.

@bastianwegge
Copy link

It is like @MarkLyck suggested. If your schema-Mock does not match what the MockedProvider is expecting, it does return undefined. I tried using one of the responses from the browser to generate a Mock and it worked!

@ben-gooding-sky
Copy link

ben-gooding-sky commented Feb 12, 2021

Having the same issue. As a workaround you may set the fetchPolicy == "no-cache" in the useQuery options:

const { loading, error, data } = useQuery(LATEST_SELL_SIGNALS, {fetchPolicy: "no-cache"})

I had this exact issue which this fixes, but I don't want to have to refetch my user both on the server and then again in the component, is there another work around for this?

Edit: After a lot of searching there's a work around adding defaultOptions parameter to the mocked provider!

<MockedProvider
        mocks={mocks}
        addTypename={false}
        defaultOptions={{ watchQuery: { fetchPolicy: 'no-cache' } }}
      >

@marcin-piechaczek
Copy link

v3.3.11, the same problem. None of the solutions mentioned above helped. Did someone manage to solve this problem differently?

@bastianwegge
Copy link

@marcin-piechaczek can you provide a testable Version of what you're working with? I'd love to take a look.

@marcin-piechaczek
Copy link

Hi @bastianwegge, thanks for the reply. I noticed that this problem must be somewhere on my side because I cannot reproduce this problem on a mocked project. I will double-check my config and let you know.

@bastianwegge
Copy link

@benjamn I guess this issue can be closed then, the original issue-creator was satisfied in September 2020 (#7081 (comment))

@marcin-piechaczek
Copy link

In my case, the SSR turned out to be the problem. I fetch the data on the server-side, then read it from the client-side cache and between the data was undefined. I solved this problem by adding a loading state that satisfies mockProvider.

Stack: Nextjs + apollo + storybook (stories with apollo hook useQuery was undefined).
Add loading state to the component and make sure that fetched data is equal to the mocked.

@msakrejda
Copy link

Same issue here. A mock call had bit-rotted and broke a story due to a missing query in an updated GraphQL operation. I updated @apollo/client to latest (3.3.13), but still no sensible error message: just loading false, error undefined, data undefined. This is super unhelpful--an error warning of invalid mocks would be much more useful here.

@VFC-elindgren
Copy link

VFC-elindgren commented Apr 3, 2021

Not sure if this is the issue others had, but after hours and hours I finally figured out our issue.

Our mocks were not providing arrays (that were typed as optional), by just adding empty arrays for those properties in the mocks, finally the response is as expected.

a) this seems like a bug
b) to echo everyone else, some minimal error message would've been sooooooo helpful here

EDIT: Turns out any prop missing (incl any optional props) is causing our data to be undefined. Our stop gap is to use this type

type RecursiveRequired<T> = T extends Object ? {
  [Property in keyof T]-?: RecursiveRequired<T[Property]>;
} : T

applied to our fragments to enforce that all props are present

@natakina
Copy link

I have the same issue with "@apollo/client": "^3.3.15",
@ben-gooding-sky Thanks a lot! I spent too much time on this issue. Your solution works great.

I have added defaultOptions={{ watchQuery: { fetchPolicy: 'no-cache' } }} to the MockedProvider and it works. Thank you.

@tarehart
Copy link

While fetchPolicy: 'no-cache' will work for most people, I was specifically trying to unit test the caching behavior of my app, so I couldn't use that fix.

Instead, I took the advice from https://levelup.gitconnected.com/gotchas-using-react-apollo-mockprovider-ec2a22a07e76:

Inject InMemoryCache to show the error message

Using that technique, I was able to fix some subtle problems in my mocked responses which had been breaking my test, and I no longer needed to rely on fetchPolicy: 'no-cache'.

@natakina
Copy link

natakina commented May 5, 2021

@tarehart Thanks for this link. It's very helpful. I have added __typename to the mocks as I use fragments in queries.
It's not obvious and there is no mention of this in the docs (or I haven't found).

@Dajust
Copy link

Dajust commented Jun 9, 2021

I've tried everything on this list, but no success still.

Testing on React Native.

@rarenatoe
Copy link

While fetchPolicy: 'no-cache' will work for most people, I was specifically trying to unit test the caching behavior of my app, so I couldn't use that fix.

Instead, I took the advice from https://levelup.gitconnected.com/gotchas-using-react-apollo-mockprovider-ec2a22a07e76:

Inject InMemoryCache to show the error message

Using that technique, I was able to fix some subtle problems in my mocked responses which had been breaking my test, and I no longer needed to rely on fetchPolicy: 'no-cache'.

Solution Number 3 of your link is what fixed it for me.
Honestly no idea why I have to do these extra steps since it seems like this should be a part of the processes that the MockedProvider takes, buuuuu...t whatever.
Dilemma deleted.

@maciejregula
Copy link

This is something which needs to be improved. For example, another scenario is when the MockedQuery should contain variables.

This returns undefined:

const mockedQuery = {
  request: {
    query: MY_QUERY,
    variables: {
      example: "someString",
    },
  },
  .
  .
  .
}

but this works fine:

const mockedQuery = {
  request: {
    query: MY_QUERY,
    variables: {
      example: "",
    },
  },
  .
  .
  .
}

This is ridiculous that "variables" values may cause that the whole data in a test is undefined.

@dustin-rcg
Copy link

dustin-rcg commented Jul 8, 2021

Please throw an error when mismatched mock shape causes NO MOCK DATA AT ALL to be supplied. That seems like an important requirement for a test component.

@leognmotta
Copy link

tried every solution but it does not work

@brunolm
Copy link

brunolm commented Oct 11, 2021

In my case I had to wait so the request could resolve

import { MockedProvider } from '@apollo/client/testing';
import { act, render, screen } from '@testing-library/react';

beforeEach(async () => {
  render(...)

  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });
});

@avoltis
Copy link

avoltis commented Dec 21, 2021

the solution for me without changing cache type on the react component, was to add __typename when needed (where we had a fragment) on the mocks

@vladrose
Copy link

I've added __typename field to my mock entities and it works, thanks all!

@danielo515
Copy link

Please consider adding an error or warning if the mock doesn't match the expected return, if this is the intended behaviour.

Indeed throwing, an ideally meaningful, error will be the safest thing to do

@jpvajda
Copy link
Contributor

jpvajda commented Jun 10, 2022

Just tagging this RFC as it's related #9738

@hesawant
Copy link

Was facing the same issue. I am using @testing-library/react for testing.

The trick is to wrap render inside act(...) and await on it.

import { act, render, screen } from '@testing-library/react';
import { MockedProvider, MockedResponse } from '@apollo/client/testing';

const mocks: MockedResponse[] = [
  {
    request: { ... },
    response: { ... },
  }
]

describe('Test cases', () => {
  it('One of the test case', async () => {
    await act(async () => {                          <<======= THIS LINE
      render(
        <MockedProvider mocks={mocks} addTypename={false}>
          <MyComponent />
        </MockedProvider>
      );
    });

    // Assert on any condition.
    expect(screen.getByTestId(...)).not.toBeNull();
  });
});

@malininss
Copy link

I have the same issue (not only in a storybook, but just in my tests). Could anyone from core team pay attention to this issue? It's been 3 years since the bug appeared

For me works two solutions:

  1. Pass defaultOptions={{ watchQuery: { fetchPolicy: 'no-cache' } }} to the MockProvider
  2. Pass prop:
        cache={
          new InMemoryCache({
            possibleTypes: {
              YourInterface: ['possibleTypeOne', 'possibleTypeTwo],
            },
          })
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests