No provider for @Inject('environment') in Unit Test with Angular & JEST

Viewed 172

I have a problem how to test one of our services writing in angular app (version 13).

This is part of the service (AdressService) which I want to test:

@Injectable({
  providedIn: 'root'
})
export class AddressService {
  private formDataSource = new BehaviorSubject<ICreateEditAddress>({
    id: null,
    company: null,
    lastName: null,
    firstName: null,
    street: null,
    streetNumber: null,
    zip: null,
    city: null,
    country: null,
    countryOptions: null,
    formFields: null
  });
  public readonly formData = this.formDataSource.asObservable();

  constructor(
    @Inject(LOCALE_ID) private language: string,
    private graphQlService: GraphQLService,
  ) {
  }
  public async getAddressDetailsForm(id?: string | number): Promise<ICreateEditAddress> {
    return this.graphQlService.performStandardQuery<IGetAddressDetailsResponse>({
      query: createEditAddressQuery,
      queryVariables: {
        lang: this.language,
        id: id || null,
      },
      relatedObservable: this.addressGetDetailsObservable,
    }).then(result => {
      if (typeof result?.data?.address === 'object') {
        this.formDataSource.next({
          ...this.filterAddressDetailsResponse(result.data.address)
        });
      }
      return result?.data?.address;
    });
  }
}

Inside GraphQLService I have:

@Injectable({
  providedIn: 'root'
})
export class GraphQLService {
  constructor(
    @Inject(LOCALE_ID) protected language: string,
    protected apollo: Apollo,
    protected error: ErrorService,
    private router: Router,
    private browser: BrowserService,
  ) {
  }
}

Finally in ErrorService I have:

@Injectable({
  providedIn: 'root'
})
export class ErrorService {
  private messageSubject = new Subject<IErrorEvent>();
  public readonly message = this.messageSubject.asObservable();
  private readonly renderer: Renderer2;

  constructor(
    @Inject('environment') public environment: any,
    private browser: BrowserService,
    rendererFactory: RendererFactory2,
  ) {
    this.renderer = rendererFactory.createRenderer(null, null);
  }
}

My test case look like:

describe('Address service', () => {
  let service: AddressService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      // imports: [AppModule], { provide: APP_BASE_HREF, useValue: '/'} - not working, generating error NotSupportedError: This name has already been registered in the registry.
      providers: [AddressService, GraphQLService, Apollo, ErrorService, Router, BrowserService], //
      // providers: [AddressService, GraphQLService, Apollo, ErrorService, Router, BrowserService] -> not working missing declaration for environment which is  @Inject('environment') public environment: any in ErrorService. This is file with configuration, one const which is exported.
      // Error: NullInjectorError: R3InjectorError(DynamicTestModule)[AddressService -> GraphQLService -> ErrorService -> environment -> environment]: 
      // NullInjectorError: No provider for environment!
    });
    service = TestBed.inject(AddressService);
  });

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

  it('should return an empty address', async () => {
    await service.getAddressDetailsForm('1');
    await service.getAddressDetailsForm();
    const form = service.getFormData();
    expect(form.city).toBeNull();
  });
});

I try on several way to use @Inject('environment') -> to solve problem. When I adding services like in example above I got error like:

NullInjectorError: R3InjectorError(DynamicTestModule)[AddressService -> GraphQLService -> ErrorService -> environment -> environment]: 
      NullInjectorError: No provider for environment!

When I remove all providers but keep only providers: [AddressServices] and add imports: [AppModule] then I got this error:

NotSupportedError: This name has already been registered in the registry.

Can you help me to solve that problem?

1 Answers

You shouldn't be importing AppModule. In unit tests, you only import/provide the single class/element you are testing, and mock any dependencies it might have.

In this case, you would provide AddressService, along with a value for LOCALE_ID and a mock of GraphQLService. Nothing more.

Related