'AngularFireModule has not been provided using v7.0.1 and new method of initializing the firebase app

I'm attempting to connect to the firebase emulator within an integration test, using the new AngularFire API (>v7)

import {
  TestBed
} from '@angular/core/testing';
import {
  initializeApp,
  provideFirebaseApp
} from '@angular/fire/app';
import {
  doc,
  enableIndexedDbPersistence,
  Firestore,
  getFirestore,
  provideFirestore,
  setDoc
} from '@angular/fire/firestore';
import {
  connectFirestoreEmulator
} from "firebase/firestore";

describe('FirestoreEmulatorSmoketest', () => {
  let projectId: string;
  let firestore: Firestore;

  beforeAll(() => {

    const testConfig = {
      projectId,
      auth: ...
    };
    TestBed.configureTestingModule({
      imports: [
        provideFirebaseApp(() => initializeApp(testConfig)),
        provideFirestore(() => {
          const firestore = getFirestore();
          connectFirestoreEmulator(firestore, 'localhost', 8080);
          enableIndexedDbPersistence(firestore);
          return firestore;
        }),
      ],
    })

  });

  beforeEach(() => {})
  afterAll(() => {})

  it('should connect', () => {
    const fooDoc = doc(firestore, "foo/12345");
    return setDoc(fooDoc, {
      updated: new Date()
    })
  })
});

This code produces the following error "AngularFireModule has not been provided"

I can only assume I'm not initialising angular fire somehow?



Solution 1:[1]

First of all, my native language is not English, so if I write like a fool you know why.

Try this.

environment.ts

export const environment = {
    production: false,
    useEmulators: true,
    firebaseConfig: {
        apiKey: 'YOUR-API-KEY',
        authDomain: 'YOUR-AUTH-DOMAIN',
        projectId: 'YOUR-PROJECT-ID',
        storageBucket: 'YPUR-STORAGE-BUCKET',
        messagingSenderId: 'YOUR-MESSAGING-SENDER-ID',
        appId: 'YOUR-APP-ID',
        measurementId: 'YOUR-MEASUREMENT-ID',
    },
};

Important: As you can see I have the variable useEmulators, which in the following lines I will explain what it is going to be used for.

app.module.ts

import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { getAuth, provideAuth, connectAuthEmulator } from '@angular/fire/auth';
import { getFirestore, provideFirestore, connectFirestoreEmulator, enableIndexedDbPersistence } from '@angular/fire/firestore';
import { getStorage, provideStorage, connectStorageEmulator } from '@angular/fire/storage';
import { getAnalytics, provideAnalytics } from '@angular/fire/analytics';
import { getFunctions, provideFunctions, connectFunctionsEmulator} from '@angular/fire/functions';
import { environment } from 'environments/environment'; // <--- Environment variables.  

imports: [
    // Firebase
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideFirestore(() => {
        if (environment.useEmulators) {
            const firestore = getFirestore();
            connectFirestoreEmulator(firestore, 'localhost', 8080);
            enableIndexedDbPersistence(firestore);
            return firestore;
        } else {
            getFirestore();
        }
    }),
    provideAuth(() => {
        if (environment.useEmulators) {
            const fireauth = getAuth();
            connectAuthEmulator(fireauth, 'http://localhost:9099'); // <---FireAuth Port
            return fireauth;
        } else {
            getAuth();
        }
    }),
    provideStorage(() => {
        if (environment.useEmulators) {
            const firestorage = getStorage();
            connectStorageEmulator(firestorage, 'localhost', 9199); // <---- Firestorage Port
            return firestorage;
        } else {
            getStorage();
        }
    }),
    provideFunctions(() => {
        if (environment.useEmulators) {
            const firefunctions = getFunctions();
            connectFunctionsEmulator(firefunctions, 'localhost', 5001); // <--- FireFunctions Port
            return firefunctions;
        } else {
            getFunctions();
        }
    }),
    provideAnalytics(() => getAnalytics()),
],

Important (Environment Path): Change the environment path of the variables in case you don't have them in the default location.

Important (Local Ports): In case you use different local ports than the default ones, change them.

As you can see I have added code to the initializations to be able to switch between the emulated project and the online project, in a simple way:

useEmulators: true // We load the emulator environment
useEmulators: false // We load the production environment

_fireAuth.service.ts

import { Auth } from '@angular/fire/auth';

constructor(private _fireAuth: Auth) {} 

_fireStorage.service.ts

import { Storage } from '@angular/fire/storage';

constructor(private _fireStorage: Storage) {} 

_fireStore.service.ts

import { Firestore } from '@angular/fire/firestore';

constructor(private _fireStore: Firestore) {} 

It only remains to import the functions you are going to use, e.g. { doc, Collection, etc... }.

Use the documentation provided by Google to see how they changed the functions: https://firebase.google.com/docs/build and use the code found in the "Web version 9 (modular)" tab.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1