Apollo-angular: Fragments error when using ApolloTestingModule

Created on 3 Jul 2018  Â·  13Comments  Â·  Source: kamilkisiela/apollo-angular

Hello,

I set up a testing suite with ApolloTestingModuleand it worked ok so far. Recently, I added a fragment to my query I got the next error and my test fails.

WARN: '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.'
HeadlessChrome 67.0.3396 (Mac OS X 10.13.5): Executed 56 of 70 SUCCESS (0 secs / 1.117 secs)
WARN: 'Could not find __typename on Fragment ', 'RepairCase', Object{id: '1', categoryId: '10.10.10', createdAt: '2018-06-29T12:00:00Z', ...}
HeadlessChrome 67.0.3396 (Mac OS X 10.13.5): Executed 56 of 70 SUCCESS (0 secs / 1.117 secs)
WARN: '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.'
HeadlessChrome 67.0.3396 (Mac OS X 10.13.5): Executed 56 of 70 SUCCESS (0 secs / 1.117 secs)
ERROR: 'WARNING: heuristic fragment matching going on!'

It doesn't depend on which query I use or which fragment I added. The test doesn't do any fancy

      imports: [
        ApolloTestingModule
      ],
...
    backend = TestBed.get(ApolloTestingController);
...
    backend.expectOne('xxx').flush(TEST_RESPONSE);

I'm using [email protected]

Most helpful comment

@kamilkisiela I thought the whole point was to not have to do that in tests.
Also, if I add __typename I will not be able to use type check on const files: FileType[] = [ ... ] because __typename is not a property in FileType

All 13 comments

any update on this?

Could you create a test for it here?

https://github.com/fetis/apollo-angular/commit/da7007342eb1728b2a57328dd9826b5c2bb19b45
here's the test which reproduces the problem.

to see the warnings you have to run the tests directly cd packages/apollo-angular && yarn test:testing

I have same problem when I use ApolloTestingModule.
The warn message as below:
console.warn node_modules/apollo-cache-inmemory/lib/bundle.umd.js:33 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:35 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.

Then I check the code in apollo-angular/testing/module.js and found that "addTypename" was false.
function ApolloTestingModule(apollo, backend) { var link = new ApolloLink(function (operation) { return backend.handle(operation); }); var cache = new InMemoryCache({ addTypename: false, }); apollo.create({ link: link, cache: cache }); }

I can not solved this problem. Anyone can help me ?

I'm going to work on a change soon. It will allow to use a custom configuration. I need to check what workaround might look like so you can solve the issue for now.

@fetis @wszhi

786 should allow to use custom cache

Here's how you can solve the problem thanks to the PR:
https://github.com/apollographql/apollo-angular/blob/ff3db3a0e6e68a2b0b0adfea57b3a03984b2058e/packages/apollo-angular/testing/tests/integration.spec.ts#L182-L230

1.3.0 is out!

@kamilkisiela your change solved the problem with addTypename being set to false with no way of changing it as pointed out by @wszhi, but it does not solve the issue from op because when you set addTypename to true, tests that were passing start to fail.

For example, this is the error I get when I set addTypename to true on one of my tests:

Expected $[0].selectionSet.selections[0].selectionSet.selections.length = 2 to equal 1.
Expected $[0].selectionSet.selections[0].selectionSet.selections[1] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
Expected $[1].selectionSet.selections.length = 6 to equal 5.
Expected $[1].selectionSet.selections[1].selectionSet.selections.length = 3 to equal 2.
Expected $[1].selectionSet.selections[1].selectionSet.selections[2] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
Expected $[1].selectionSet.selections[5] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.

@adgoncal could you provide an example?

@kamilkisiela

apollo-test.service.ts

import { Injectable } from '@angular/core';
import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

import { Observable, from } from 'rxjs';
import { pluck } from 'rxjs/operators';

export interface FileType {
  id: string;
  name: string;
  type: string;
  extension: string;
  createDate: string;
  blob: string;
}

export const FileFragment = `
  fragment fileFields on File {
    id
    name
    type
    extension
    createDate
    blob
  }
`;

export const FileQuery = gql`
  query files($directoryId: ID!) {
    files(directoryId: $directoryId) {
      ...fileFields
    }
  }
  ${FileFragment}
`;

@Injectable({
  providedIn: 'root'
})
export class ApolloTestService {
  constructor(private apollo: Apollo) {}

  getFiles(directoryId: string): Observable<FileType[]> {
    const query = FileQuery;
    const variables = {
      directoryId,
    };

    const files$ = this.apollo.query<any>({ query, variables });

    return from(files$).pipe(pluck('data', 'files'));
  }
}

apollo-test.service.spec.ts

import { TestBed } from '@angular/core/testing';
import {
  ApolloTestingModule,
  ApolloTestingController,
  APOLLO_TESTING_CACHE,
} from 'apollo-angular/testing';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { Observable, of } from 'rxjs';

import {
  ApolloTestService,
  FileType,
  FileQuery,
} from './apollo-test.service';

describe('ApolloTestService', () => {
  let service: ApolloTestService;
  let backend: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
    });

    backend = TestBed.get(ApolloTestingController);
    service = TestBed.get(ApolloTestService);
  });

  afterEach(() => {
    backend.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('getFiles', () => {
    it('should return Observable<FileType[]>', (done: DoneFn) => {
      const directoryId = 'directory id';
      const files: FileType[] = [
        {
          id: 'file id',
          name: 'file name',
          type: 'file type',
          extension: 'ext',
          createDate: new Date().toISOString(),
          blob: 'blob value',
        }
      ];
      const op = {
        query: FileQuery,
        variables: {
          directoryId,
        },
        operationName: 'files',
      };

      const files$ = service.getFiles(directoryId);

      files$.subscribe({
        next: result => {
          expect(result).toEqual(files);
          done();
        },
        error: e => {
          done.fail(e);
        },
      });

      backend
        .expectOne(operation => {
          expect(operation.operationName).toBe(op.operationName);
          expect(operation.variables).toEqual(op.variables);
          expect(operation.query.definitions).toEqual(op.query.definitions);
          return true;
        })
        .flush({ data: { files } });
    });
  });
});

describe('ApolloTestService with custom cache', () => {
  let service: ApolloTestService;
  let backend: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
      providers: [
        {
          provide: APOLLO_TESTING_CACHE,
          useValue: new InMemoryCache({addTypename: true}),
        },
      ],
    });

    backend = TestBed.get(ApolloTestingController);
    service = TestBed.get(ApolloTestService);
  });

  afterEach(() => {
    backend.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('getFiles', () => {
    it('should return Observable<FileType[]>', (done: DoneFn) => {
      const directoryId = 'directory id';
      const files: FileType[] = [
        {
          id: 'file id',
          name: 'file name',
          type: 'file type',
          extension: 'ext',
          createDate: new Date().toISOString(),
          blob: 'blob value',
        }
      ];
      const op = {
        query: FileQuery,
        variables: {
          directoryId,
        },
        operationName: 'files',
      };

      const files$ = service.getFiles(directoryId);

      files$.subscribe({
        next: result => {
          expect(result).toEqual(files);
          done();
        },
        error: e => {
          done.fail(e);
        },
      });

      backend
        .expectOne(operation => {
          expect(operation.operationName).toBe(op.operationName);
          expect(operation.variables).toEqual(op.variables);
          expect(operation.query.definitions).toEqual(op.query.definitions);
          return true;
        })
        .flush({ data: { files } });
    });
  });
});

Test results

$ ng test --watch=false --no-progress
06 10 2018 15:40:36.161:INFO [karma]: Karma v3.0.0 server started at http://0.0.0.0:9876/
06 10 2018 15:40:36.738:INFO [launcher]: Launching browser Chrome with unlimited concurrency
06 10 2018 15:40:36.745:INFO [launcher]: Starting browser Chrome
06 10 2018 15:40:42.388:INFO [Chrome 69.0.3497 (Windows 10 0.0.0)]: Connected on socket Vhf0Dc6zagXZW8tcAAAA with id 77258880
WARN: '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.'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: '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
WARN: 'Could not find __typename on Fragment ', 'File', Object{id: 'file id', name: 'file name', type: 'file type', extension: 'ext', createDate: '2018-10-06T20:40:42.897Z', blob: 'blob value'}
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: 'Could not find __typename on Fragment ', 'File', Object{id: 'file id', name: 'file name', type: 'file type', extension: 'ext', createDate: '2018-10-06T20:40:42.897Z', blob: 'blob valuWARN: '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.'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: '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 truERROR: 'WARNING: heuristic fragment matching going on!'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
ERROR: 'WARNING: heuristic fragment matching going on!'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: 'Missing field __typename in {
  "id": "file id",
  "name": "file name",
  "type": "file type",
  "extension": "ext",
  "createDa'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: 'Missing field __typename in {
  "id": "file id",
  "name": "file name",
  "type": "file type",
  "extension": "ext",
WARN: 'Missing field __typename in {
  "id": "file id",
  "name": "file name",
  "type": "file type",
  "extension": "ext",
  "createDa'
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
WARN: 'Missing field __typename in {
  "id": "file id",
  "name": "file name",
  "type": "file type",
  "extension": "ext",
Chrome 69.0.3497 (Windows 10 0.0.0) ApolloTestService with custom cache getFiles should return Observable<FileType[]> FAILED
        Expected $[0].selectionSet.selections[0].selectionSet.selections.length = 2 to equal 1.
        Expected $[0].selectionSet.selections[0].selectionSet.selections[1] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
        Expected $[1].selectionSet.selections.length = 7 to equal 6.
        Expected $[1].selectionSet.selections[6] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
            at eval (./src/app/apollo-test.service.spec.ts?:123:53)
            at eval (./node_modules/apollo-angular/fesm5/ng.apollo.testing.js?:160:64)
            at Array.filter (<anonymous>)
            at ApolloTestingBackend._match (./node_modules/apollo-angular/fesm5/ng.apollo.testing.js?:160:30)
        Failed: Cannot read property 'files' of null
        TypeError: Cannot read property 'files' of null
            at MapSubscriber.mapper [as project] (./node_modules/rxjs/_esm5/internal/operators/pluck.js?:21:32)
            at MapSubscriber._next (./node_modules/rxjs/_esm5/internal/operators/map.js?:40:35)
            at MapSubscriber.Subscriber.next (./node_modules/rxjs/_esm5/internal/Subscriber.js?:63:18)
            at eval (./node_modules/apollo-angular/fesm5/ng.apollo.js?:37:28)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:387:26)
            at ProxyZoneSpec.onInvoke (./node_modules/zone.js/dist/zone-testing.js?:287:39)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:386:32)
            at Zone.run (./node_modules/zone.js/dist/zone.js?:137:43)
            at eval (./node_modules/zone.js/dist/zone.js?:871:34)
            at ZoneDelegate.invokeTask (./node_modules/zone.js/dist/zone.js?:420:31)
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 4 of 4 (1 FAILED) (0 secs / 0.138 secs)
Chrome 69.0.3497 (Windows 10 0.0.0) ApolloTestService with custom cache getFiles should return Observable<FileType[]> FAILED
        Expected $[0].selectionSet.selections[0].selectionSet.selections.length = 2 to equal 1.
        Expected $[0].selectionSet.selections[0].selectionSet.selections[1] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
        Expected $[1].selectionSet.selections.length = 7 to equal 6.
        Expected $[1].selectionSet.selections[6] = Object({ name: Object({ value: '__typename', kind: 'Name' }), kind: 'Field' }) to equal undefined.
            at eval (./src/app/apollo-test.service.spec.ts?:123:53)
            at eval (./node_modules/apollo-angular/fesm5/ng.apollo.testing.js?:160:64)
            at Array.filter (<anonymous>)
            at ApolloTestingBackend._match (./node_modules/apollo-angular/fesm5/ng.apollo.testing.js?:160:30)
        Failed: Cannot read property 'files' of null
        TypeError: Cannot read property 'files' of null
            at MapSubscriber.mapper [as project] (./node_modules/rxjs/_esm5/internal/operators/pluck.js?:21:32)
            at MapSubscriber._next (./node_modules/rxjs/_esm5/internal/operators/map.js?:40:35)
            at MapSubscriber.Subscriber.next (./node_modules/rxjs/_esm5/internal/Subscriber.js?:63:18)
            at eval (./node_modules/apollo-angular/fesm5/ng.apollo.js?:37:28)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:387:26)
            at ProxyZoneSpec.onInvoke (./node_modules/zone.js/dist/zone-testing.js?:287:39)
            at ZoneDelegate.invoke (./node_modules/zone.js/dist/zone.js?:386:32)
            at Zone.run (./node_modules/zone.js/dist/zone.js?:137:43)
            at eval (./node_modules/zone.js/dist/zone.js?:871:34)
Chrome 69.0.3497 (Windows 10 0.0.0): Executed 4 of 4 (1 FAILED) (0.213 secs / 0.138 secs)
TOTAL: 1 FAILED, 3 SUCCESS
TOTAL: 1 FAILED, 3 SUCCESS

Other information:

$ ng --version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / â–³ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 6.2.4
Node: 10.10.0
OS: linux x64
Angular: 6.1.9
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.8.4
@angular-devkit/build-angular     0.8.4
@angular-devkit/build-optimizer   0.8.4
@angular-devkit/build-webpack     0.8.4
@angular-devkit/core              0.8.4
@angular-devkit/schematics        0.8.4
@angular/cli                      6.2.4
@ngtools/webpack                  6.2.4
@schematics/angular               0.8.4
@schematics/update                0.8.4
rxjs                              6.2.2
typescript                        2.9.2
webpack                           4.20.2

@adgoncal if you turned addTypename on why you don't define __typename fields in tests?

@kamilkisiela I thought the whole point was to not have to do that in tests.
Also, if I add __typename I will not be able to use type check on const files: FileType[] = [ ... ] because __typename is not a property in FileType

Was this page helpful?
0 / 5 - 0 ratings