kamilkisiela / apollo-angular

A fully-featured, production ready caching GraphQL client for Angular and every GraphQL server 🎁
https://apollo-angular.com
MIT License
1.5k stars 309 forks source link

Failed: R3InjectorError(DynamicTestModule)[MyService-> Apollo -> Apollo]: NullInjectorError: No provider for Apollo! #1846

Closed Nexplore-jcsilvahernandez closed 1 year ago

Nexplore-jcsilvahernandez commented 1 year ago

Describe the bug Updated to apollo-angular@4.1.1 library in an old Angular application using v10, migrated it to 14, ran the test suite again, and this shows up:

Chrome Headless 107.0.5304.107 (Windows 10) MysService#request should return networkError error FAILED
        Failed: R3InjectorError(DynamicTestModule)[MyService-> Apollo -> Apollo]:
          NullInjectorError: No provider for Apollo!
        error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'MyService', 'Apollo', 'Apollo' ] })
            at NullInjector.get (node_modules/@angular/core/fesm2015/core.mjs:6344:27)
            at R3Injector.get (node_modules/@angular/core/fesm2015/core.mjs:6771:33)
            at R3Injector.get (node_modules/@angular/core/fesm2015/core.mjs:6771:33)
            at injectInjectorOnly (node_modules/@angular/core/fesm2015/core.mjs:4761:33)
            at ɵɵinject (node_modules/@angular/core/fesm2015/core.mjs:4765:60)
            at Object.factory (ng:///MyService/ɵfac.js:4:47)
            at R3Injector.hydrate (node_modules/@angular/core/fesm2015/core.mjs:6872:35)
            at R3Injector.get (node_modules/@angular/core/fesm2015/core.mjs:6760:33)
            at TestBedImpl.inject (node_modules/@angular/core/fesm2015/testing.mjs:26170:52)
            at Function.inject (node_modules/@angular/core/fesm2015/testing.mjs:26036:37)

The rest of the test suite works though. I read in another closed issue that this bug was solved as per 3.0.1, so why did this pop up again?

To Reproduce Try to launch any Apollo test only specifying the ApolloTestingModule in the imports

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

      controller = TestBed.inject(ApolloTestingController);
    })
  );

Expected behavior Tests should launch without throwing any error

Environment:

- @angular/cli@14.2.9
- @angular/core@14.2.11
- @apollo/client@3.7.1
- apollo-angular@4.1.1
- graphql@16.6.0
- typescript@4.8.4
PowerKiKi commented 1 year ago

Your code example looks pretty much exactly like our tests in: https://github.com/kamilkisiela/apollo-angular/blob/06b1ba18602a7ededbef904af27de139c81019a2/packages/apollo-angular/testing/tests/integration.spec.ts#L17

And since our tests pass, there might be something else going on in your project. I'd suggest you either simplify your project to the extreme to see when it starts working or create a PR in this project with a failing test that demonstrate your issue.

Nexplore-jcsilvahernandez commented 1 year ago

Ok I'll be more specific. The code below is the test file for MyService

describe('MyService', () => {
  let controller: ApolloTestingController;
  let service: MyService;
  let blobName: string;

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

      controller = TestBed.inject(ApolloTestingController);
      service = TestBed.inject(MyService);
      blobName = 'path/to/blob';
    })
  );

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

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

When reaching the part where is checks the service toBeTruthy() is when the error is thrown and displays the message No provider for Apollo. MyService has a constructor that extends a base service which has Apollo as a parameter, so TestBed.inject(MyService) should resolve all the dependency tree unless some of them are non injectable. And even if they were non injectable I tried several combinations, like instancing apollo separately with let apollo = new Apollo() and constructing the service afterwards with new MyService(apollo), but the same error pops up. I cannot simplify it further.

Nexplore-jcsilvahernandez commented 1 year ago

I've managed to get it working by importing both ApolloTestingModule AND ApolloModule like so:

describe('MyService', () => {
  let controller: ApolloTestingController;
  let service: MyService;
  let blobName: string;

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

      controller = TestBed.inject(ApolloTestingController);
      service = TestBed.inject(MyService);
      blobName = 'path/to/blob';
    })
  );

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

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

I don't think this is the intended behaviour but if you say otherwise I will close this issue.

EDIT: Okay it doesn't work when I uncomment the actual tests that call the underlying service's apollo class, it throws a bunch of different errors, to list a few: Error: Client has not been defined yet Error: An asynchronous beforeAll or afterAll function called its 'done' callback more than once. Expected 'Client has not been defined yet' to contain 'Testing Network Error Message'. Error: Expected one matching operation for criteria "Match DocumentNode", found none.

HendrikJanssen commented 1 year ago

Can confirm this behaviour. When we import the ApolloModule to get rid of the No provider for Apollo error, we get the following error:

Error: Expected one matching operation for criteria "Match DocumentNode", found none.

    at ApolloTestingBackend.expectOne ([...]/node_modules/apollo-angular/build/fesm2015/ngApolloTesting.mjs:135:19)

Current workaround seems to be to downgrade to v4.1.0 This allows for tests to pass.

Seems like the 4.1.1 update broke something.

PowerKiKi commented 1 year ago

It would be nice if one of you could create a PR in this project with a failing test that demonstrate your issue. So we can work on a solution together and prevent regression in the future.

HendrikJanssen commented 1 year ago

I will see if I can provide an example this evening 👍

Nexplore-jcsilvahernandez commented 1 year ago

@HendrikJanssen Can confirm that downgrading to v4.1.0 solves the issue and allows the CI to run the tests again :)

PowerKiKi commented 1 year ago

I am assuming this should be solved by 4.2.0. Re-open if that's not the case.