Intended outcome:
When I run my tests I expect them to pass without an error .
Actual outcome:
I get a massive warning about using fragments without __typename
You're using fragments in your queries, but either don't have the addTypename:
true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
can accurately match fragments.
Here are my tests:
apolloProxy.test.js
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';
import { withCurrentUser, withArticlesByUser, CURRENT_USER_QUERY, ARTICLES_BY_USER_QUERY } from './apolloProxy';
import {
CURRENT_USER_QUERY_DATA as mockedUserData,
ARTICLES_BY_USER_DATA as mockedArticlesByUserData,
} from './__mocks__/data';
// source: https://stackoverflow.com/questions/45700550/how-to-use-a-mocked-data-with-react-apollo-for-tests
/*
Note: By using a Dummy component you just test your graphql()
Queries and Mutations and the way you have configured them
(options, props, skip, variables, etc.) So you don't mount your
ctual React components. It's better to test those in their 'unconnected' state.
*/
describe('Testing GraphQL Queries', () => {
let done;
let query;
class Dummy extends Component {
componentDidMount() {
const { loading, data } = this.props.data;
expect(loading).toBe(true);
expect(data).toBe(undefined);
}
componentWillReceiveProps(nextProps) {
switch (query) {
case 'CURRENT_USER_QUERY': {
const { loading, user } = nextProps.data;
expect(loading).toBe(false);
expect(user).toEqual(mockedUserData.data.user);
break;
}
case 'ARTICLES_BY_USER_QUERY': {
const { loading, user } = nextProps.data;
expect(loading).toBe(false);
expect(user).toEqual(mockedArticlesByUserData.data.user);
break;
}
default:
break;
}
done();
}
render() {
return null;
}
}
it('Check Current User Query', (doneCallback) => {
done = doneCallback;
query = 'CURRENT_USER_QUERY';
const variables = { cache: false };
const DummyWithCurrentUser = withCurrentUser(Dummy);
const mocks = [
{
request: { query: CURRENT_USER_QUERY, variables },
result: { data: mockedUserData.data },
},
];
mount(<MockedProvider removeTypename mocks={mocks}><DummyWithCurrentUser /></MockedProvider>);
});
it('Check Articles by User Query', (doneCallback) => {
done = doneCallback;
query = 'ARTICLES_BY_USER_QUERY';
const variables = { cache: false };
const DummyWithArticlesByUser = withArticlesByUser(Dummy);
const mocks = [
{
request: { query: ARTICLES_BY_USER_QUERY, variables },
result: { data: mockedArticlesByUserData.data },
},
];
mount(<MockedProvider removeTypename mocks={mocks}><DummyWithArticlesByUser /></MockedProvider>);
});
});
Here are my queries:
apolloProxy.js
const fragments = {
nodeArticle: gql`
fragment ArticleFields on NodeArticle{
author:entityOwner{
name
},
title,
body {
value
},
nid,
uuid
images:fieldMediaImage{
mid:targetId,
... on FieldNodeFieldMediaImage {
entity{
... on MediaImage {
image:fieldImage {
derivative(style:medium) {
url
}
}
}
}
}
}
}
`,
};
export const CURRENT_USER_QUERY = gql`
query{
user: currentUserContext{
uid,
uuid
}
}
`;
export const currentUser = () => apolloClient.query({
query: CURRENT_USER_QUERY,
});
export const withCurrentUser = graphql(CURRENT_USER_QUERY);
export const ARTICLES_BY_USER_QUERY = gql`
query articlesByUserQuery{
user:currentUserContext{
...on User{
uid
nodes:reverseUidNode(offset:0, limit:1000){
articles: entities{
... ArticleFields
}
}
}
}
}
${fragments.nodeArticle}
`;
export const articlesByUser = () => apolloClient.query({
query: ARTICLES_BY_USER_QUERY,
fetchPolicy: 'network-only',
});
export const withArticlesByUser = graphql(ARTICLES_BY_USER_QUERY);
Here is my mock data.js:
export const CURRENT_USER_QUERY_DATA = {
data: {
user: {
uid: 1,
uuid: '4e9af657-689b-4c06-8721-e267914f2255',
},
},
};
export const ARTICLES_BY_USER_DATA = {
data: {
user: {
uid: 1,
nodes: {
articles: [
{
author: {
name: 'admin',
},
title: 'hello article update',
body: null,
nid: 13,
uuid: '79502776-61f8-4c48-b464-d94eebe0e01b',
images: [],
},
{
author: {
name: 'admin',
},
title: 'asdfads',
body: {
value: '<p>asdfasdf</p>\r\n',
},
nid: 14,
uuid: 'be510cd5-645e-4f72-8baa-06cf89f53f84',
images: [],
},
],
},
},
},
};
Version
"apollo-cache-inmemory": "^1.1.5",
"apollo-client": "^2.0.3",
"apollo-client-preset": "^1.0.5",
"apollo-link": "^1.1.0",
"apollo-link-http": "^1.3.3",
I have tried adding __typename throughout my fragment and my query but the warning persists. My tests pass, but I'd like to get rid of the warnings if possible.
Reference where I found the above approach:
https://stackoverflow.com/questions/45700550/how-to-use-a-mocked-data-with-react-apollo-for-tests#
Wondering if I need to add an optimisticResponse???
Thanks for any direction
@justinlevi I am running into this exact same issue, and haven't been able to find a solution anywhere. Did you happen to ever find anything?
Following this blog post from Apollo also getting these errors/warnings. I'm also using fragments in my queries. I do have the prop in MockProvider as true.
I'm running into the same issue too, tried to resolve using removeTypename in my MockedProvider, addTypename={false} in MockedProvider, tried putting __typename in my fragment, no joy though. Did anyone find how to move past this?
@mptap I ended up solving this by hand rolling my own MockedProvider with an IntrospectionFragmentMatcher (Apollo Docs on using fragments), and with __typename in the mocked data:
import React from 'react'
import { render } from 'react-testing-library'
import wait from 'waait'
import { ApolloProvider } from 'react-apollo'
import { MockLink } from 'react-apollo/test-utils'
import { ApolloClient } from 'apollo-client'
import {
InMemoryCache,
IntrospectionFragmentMatcher,
} from 'apollo-cache-inmemory'
import PostsQuery, { query } from './PostsQuery'
import introspectionQueryResultData from 'fragmentTypes.json'
const getCacheForApollo = () => {
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
})
return new InMemoryCache({ fragmentMatcher })
}
const getTestClient = mocks => {
const cache = getCacheForApollo()
const link = new MockLink(mocks, true)
return new ApolloClient({
link,
cache,
})
}
class ResultStore {
constructor() {
this.results = {}
}
store = ({ loading, ...props }) => {
if (!props.loading) {
this.results = props
}
return null
}
}
it('sorts return types by date', async () => {
const resultStore = new ResultStore()
const mocks = [
{
request: {
query,
},
result: {
data: {
posts: [...],
},
},
},
]
render(
<ApolloProvider client={getTestClient(mocks)}>
<PostsQuery>{resultStore.store}</PostsQuery>
</ApolloProvider>
)
await wait(0)
expect(resultStore.results.posts).toEqual([...])
})
Hope this helps!
Any updates here? The only solution for me, and for now is to add __typename to every fragment inside entire application:
fragment CommonUserId on CommonUser {
id
__typename
}
But... why I need to add typename only for fragments but not to queries and mutations? :)
Also, addTypename is set to true
Having the same issue in testing. Not sure what the best solution is, as addTypename doesn't seem to have any effect
Hey any update on this? I'm having the same issue testing. Here's my test file.
/* eslint-env jest */
import React from 'react';
import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';
import ServiceSection from '../../components/ServiceSection';
import { GET_SERVICES } from '../../queries';
const servicesMock = {
request: {
query: GET_SERVICES
},
result: {
data: {
services: {
all: [
{
title: 'Servie 1',
slug: 'service-1',
excerpt: 'Some Excerpt',
content: '<p>Some Content</p>',
featuredImage: {
sourceUrl: 'http://test.com/service-1.png',
mediaDetails: {
sizes: [
{
name: 'medium_large',
sourceUrl: 'http://test.com/service-1.png',
width: 700
},
{
name: 'large',
sourceUrl: 'http://test.com/service-1.png',
width: 700
}
]
}
}
},
{
title: 'Servie 2',
slug: 'service-2',
excerpt: 'Some Excerpt 2',
content: '<p>Some Content 2</p>',
featuredImage: {
sourceUrl: 'http://test.com/service-2.png',
mediaDetails: {
sizes: [
{
name: 'medium_large',
sourceUrl: 'http://test.com/service-2.png',
width: 700
},
{
name: 'large',
sourceUrl: 'http://test.com/service-2.png',
width: 800
}
]
}
}
}
]
}
}
}
};
describe('Services Section', () => {
it('renders when given query', async () => {
const wrapper = mount(
<MockedProvider mocks={[servicesMock]} addTypename={false}>
<ServiceSection />
</MockedProvider>
);
await new Promise(resolve => setTimeout(resolve));
wrapper.update();
});
});
And here's the errors I get
yarn run v1.10.1
warning ../../../package.json: No license field
$ NODE_ENV=test jest --verbose __tests__/components/services.test.js
PASS __tests__/components/services.test.js
Services Section
โ renders when given query (56ms)
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
You're using fragments in your queries, but either don't have the addTypename:
true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
can accurately match fragments.
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
Could not find __typename on Fragment Service { title: 'Servie 1',
slug: 'service-1',
excerpt: 'Some Excerpt',
content: '<p>Some Content</p>',
featuredImage:
{ sourceUrl: 'http://test.com/service-1.png',
mediaDetails: { sizes: [Array] } } }
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.
console.error node_modules/apollo-cache-inmemory/lib/bundle.umd.js:786
WARNING: heuristic fragment matching going on!
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
Missing field __typename in {
"title": "Servie 1",
"slug": "service-1",
"excerpt": "Some Excerpt",
"content": "<p>Some C
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
Missing field id in {
"title": "Servie 1",
"slug": "service-1",
"excerpt": "Some Excerpt",
"content": "<p>Some C
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
You're using fragments in your queries, but either don't have the addTypename:
true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
can accurately match fragments.
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
Could not find __typename on Fragment Service { title: 'Servie 2',
slug: 'service-2',
excerpt: 'Some Excerpt 2',
content: '<p>Some Content 2</p>',
featuredImage:
{ sourceUrl: 'http://test.com/service-2.png',
mediaDetails: { sizes: [Array] } } }
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.
console.error node_modules/apollo-cache-inmemory/lib/bundle.umd.js:786
WARNING: heuristic fragment matching going on!
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
Missing field __typename in {
"title": "Servie 2",
"slug": "service-2",
"excerpt": "Some Excerpt 2",
"content": "<p>Some
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:761
Missing field id in {
"title": "Servie 2",
"slug": "service-2",
"excerpt": "Some Excerpt 2",
"content": "<p>Some
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
You're using fragments in your queries, but either don't have the addTypename:
true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
can accurately match fragments.
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
Could not find __typename on Fragment Service { title: 'Servie 1',
slug: 'service-1',
excerpt: 'Some Excerpt',
featuredImage:
{ type: 'id',
generated: true,
id: '$ROOT_QUERY.services.nodes.0.featuredImage',
typename: undefined } }
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:51
You're using fragments in your queries, but either don't have the addTypename:
true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
can accurately match fragments.
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:52
Could not find __typename on Fragment Service { title: 'Servie 2',
slug: 'service-2',
excerpt: 'Some Excerpt 2',
featuredImage:
{ type: 'id',
generated: true,
id: '$ROOT_QUERY.services.nodes.1.featuredImage',
typename: undefined } }
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:53
DEPRECATION WARNING: using fragments without __typename is unsupported behavior and will be removed in future versions of Apollo client. You should fix this and set addTypename to true now.
This is quite an annoying problem, what is the update on this?
This looks to be an accurate warning about misusing fragments without a fragmentMatcher.
Here is an example bit of code I use to setup my provider:
<MockedProvider cache={createApolloCache(require('../../../../fragmentTypes.json')}>
with
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import { IntrospectionResultData } from 'apollo-cache-inmemory/lib/types'
export default function createApolloCache(introspectionQueryResultData: object) {
return new InMemoryCache({
fragmentMatcher: new IntrospectionFragmentMatcher({
introspectionQueryResultData: introspectionQueryResultData as IntrospectionResultData,
}),
})
}
Thanks for the response. Is there any way we could get this requirement reflected/further explained in documentation somewhere?
Please feel free to PR a change to the component docs or the site docs and add me as a reviewer, I'll be happy to take a look at what you come up with.
So I've read the comments but I don't see the solution. I tried @rosskevin's solution
but it then rendered empty components.
I also tried adding
defaultOptions={{
watchQuery: {
fetchPolicy: 'network-only',
},
}}
but had same issue
We use IntrospectionFragmentMatcher in production, but had just gotten away with using <MockedProvider addTypename={false}> in tests to avoid needing to add __typeName keys to every stub in our tests. At a certain point that stopped working with Fragments, as described in the comments above.
I found a solution to avoid updating all the stubs with __typename:
import {
IntrospectionFragmentMatcher,
InMemoryCache,
} from 'apollo-cache-inmemory';
import introspectionQueryResultData from '../../constants/fragmentTypes.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
// ...then in the test:
<MockedProvider
mocks={mocks}
addTypename={false}
cache={
new InMemoryCache({
addTypename: false,
fragmentMatcher,
})
}
>
Setting addTypename to false both as a prop on MockedProvider and in the InMemoryCache() call was the key to getting things to pass.
Hope that helps ๐
@madisonbullard where does fragmentTypes.json come from?
May be a little bit dangerous and I haven't test it extensivelly, but instead of putting __typename everywhere in the mocks, I have just used super simple fragment matcher which don't complaints ๐
const happyFragmentMatcher = { match: () => true }
<MockedProvider
mocks={mocks}
addTypename={false}
cache={
new InMemoryCache({
addTypename: false,
fragmentMatcher: happyFragmentMatcher,
})
}
>
@madisonbullard where does
fragmentTypes.jsoncome from?
@ryanirilli You can read about GraphQL introspection here: https://graphql.org/learn/introspection/
We copy/paste the results of a __schema query into our fragmentTypes.json file. Our query looks like this:
{
__schema {
types {
kind
name
possibleTypes {
name
}
}
}
}
We use
IntrospectionFragmentMatcherin production, but had just gotten away with using<MockedProvider addTypename={false}>in tests to avoid needing to add__typeNamekeys to every stub in our tests. At a certain point that stopped working with Fragments, as described in the comments above.I found a solution to avoid updating all the subs with
__typename:import { IntrospectionFragmentMatcher, InMemoryCache, } from 'apollo-cache-inmemory'; import introspectionQueryResultData from '../../constants/fragmentTypes.json'; const fragmentMatcher = new IntrospectionFragmentMatcher({ introspectionQueryResultData, }); // ...then in the test: <MockedProvider mocks={mocks} addTypename={false} cache={ new InMemoryCache({ addTypename: false, fragmentMatcher, }) } >Setting
addTypenametofalse_both_ as a prop onMockedProviderand in theInMemoryCache()call was the key to getting things to pass.Hope that helps ๐
I tried this example. I generated the fragmentTypes and added addTypename to false, but in my tests the request is failing trying to find the __typename in the object. Is there a solution for it?
Same as above - if I remove __typename from my query, my test seems to fail.
This looks to be an accurate warning about misusing fragments without a
fragmentMatcher.Here is an example bit of code I use to setup my provider:
<MockedProvider cache={createApolloCache(require('../../../../fragmentTypes.json')}>with
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory' import { IntrospectionResultData } from 'apollo-cache-inmemory/lib/types' export default function createApolloCache(introspectionQueryResultData: object) { return new InMemoryCache({ fragmentMatcher: new IntrospectionFragmentMatcher({ introspectionQueryResultData: introspectionQueryResultData as IntrospectionResultData, }), }) }
Perhaps there's something missing in this answer but it's not working as is.
I tried @madisonbullard's suggestion to set addTypename to false in the InMemoryCache as well but as pointed by others, it's now throwing the following error:
Error: Network error: Error writing result to store for query: ...
Cannot match fragment because __typename property is missing: ...
I'm having the same as @Zhouzi, worth to say; the MockProvider now does return (generate?) some data, but it never reaches the component due to the failed write.
I also had the same as @Zhouzi and @smejier, is there a solution for this?
Also tried to pass addTypename: false to both MockedProvider and InMemoryCache, but but the issue still persists.
I just disabled my caching since cache takes the __typename and id
<MockedProvider
mocks={ ... }
addTypename={false}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
> ...
I just disabled my caching since cache takes the __typename and id
<MockedProvider mocks={ ... } addTypename={false} defaultOptions={{ watchQuery: { fetchPolicy: 'no-cache' }, query: { fetchPolicy: 'no-cache' }, }} > ...
I confirm that adding the defaultOptions as suggested by @deltek-rossjackson got rid of the warnings, thanks ๐ I guess we just have to be careful that our tests are not relying on the cache, which is probably not the case.
Most helpful comment
This is quite an annoying problem, what is the update on this?