简体   繁体   中英

How to reset firestore emulator in integration test

I'm running my integration-tests in flutter and dart using the firestore emulator. First I start the firestore emulator with some data like so: firebase emulators:start --import=./dir.

Then I start an android emulator and start the app I want to test on the android emulator. The app is configured to use the firestore emulator. Then I run a series of tests, which all write to the firestore emulator.

But on the beginning of each test, I want the data to be reset to the state, when I first started the emulator. So eg if the tests are executed in this order:

Test A Test B Test C

I don't want to have the data, Test A created to be present in the database, when Tests B and C are executed. I could terminate the firestore emulator and start it again at the beginning of each test. But this would make my tests a lot slower.

Do you know of a way to reset the data, which is present in the firebase emulator?

I am assuming you're referring to firestore when you say you want to 'reset the data'.

Per the documentation at https://firebase.google.com/docs/emulator-suite/install_and_configure#use_the_emulator_hub_rest_api

import fetch from 'node-fetch';

import firebaseConfig from '../../../firebase.json';
const hubEmulatorPort = firebaseConfig.emulators.hub.port;
const firestoreEmulatorPort = firebaseConfig.emulators.firestore.port;

async function clearDb() {
  const response = await fetch(
    `http://localhost:${firestoreEmulatorPort}/emulator/v1/projects/${process.env.PROJECT_ID}/databases/(default)/documents`,
    {
      method: 'DELETE',
    }
  );
  if (response.status !== 200) {
    throw new Error('Trouble clearing Emulator: ' + (await response.text()));
  }
}

async function populateDb(data) {
  // Logic for adding in any data you want in the db
  // before each test run
}

async function enableBackgroundTriggers() {
  const response = await fetch(`http://localhost:${hubEmulatorPort}/functions/enableBackgroundTriggers`, {
    method: 'PUT',
  });
  if (response.status !== 200) {
    throw new Error('Trouble enabling database triggers in emulator: ' + (await response.text()));
  }
}

async function disableBackgroundTriggers() {
  const response = await fetch(`http://localhost:${hubEmulatorPort}/functions/disableBackgroundTriggers`, {
    method: 'PUT',
  });
  if (response.status !== 200) {
    throw new Error('Trouble disabling database triggers in emulator: ' + (await response.text()));
  }
}

async function resetDb(data) {
  await disableBackgroundTriggers();
  await clearDb();
  await populateDb(data);
  await enableBackgroundTriggers();
}

export { resetDb };

I can't find a source for the clearing of the db, but the RESTful call in clearDb does what you want.

It's important to disable the triggers before clearing or populating the database, in case you have firestore triggers that modify data in ways your tests don't expect. I write tests by passing full DB state to the populateDb method, then reenable triggers before running tests so I can test said triggers. If you aren't running any firestore triggers, the clearDb call alone should be enough for your purposes.

My tests all have calls to resetDb() in my beforeEach hook in jest to ensure clean runs of each test. I recommend adding this to whatever 'beforeEach'-like hook your favorite testing API exposes.

If your tests do things like create users in Firebase Authentication you'll have to find another way to clear them between test runs.

If anyone can find documentation on how to clear other emulators in the Firebase Emulator Suite, please drop it in the comments. I am currently trying to find a way to clear Authentication emulators, which is actually how I found this question.

Best of luck!

If you want to clear out all the collections programatically, like in a setUp() or tearDown() there's a reference for that here: Delete data from Cloud Firestore - Delete Collections

Note that it's not recommended for all implementations, but there are examples in Java, Python, Node.js, go, PHP, C#, and Ruby.

Here's an example of how to iterate through all your collections and delete them all in Java, using the deleteCollection() method from that link.

public static void main(String[] args) throws IOException {
    final int BATCH_SIZE = 5;

    Firestore firestore = initializeCloudFirestore();

    for (CollectionReference listCollection : firestore.listCollections()) {
      deleteCollection(listCollection, BATCH_SIZE);
    }
  }

  /**
   * One way of initializing Firestore,
   * see other options at https://firebase.google.com/docs/firestore/quickstart#initialize
   */
  private static Firestore initializeCloudFirestore() throws IOException {
    // Use the application default credentials
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    FirebaseOptions options = new FirebaseOptions.Builder()
        .setCredentials(credentials)
        .setProjectId("projectId")
        .build();
    FirebaseApp.initializeApp(options);

    Firestore firestore = FirestoreClient.getFirestore();
    return firestore;
  }

  /**
   * Delete a collection in batches to avoid out-of-memory errors. Batch size may be tuned based on
   * document size (atmost 1MB) and application requirements.
   * See https://firebase.google.com/docs/firestore/manage-data/delete-data#java_5
   */
  static void deleteCollection(CollectionReference collection, int batchSize) {
    try {
      // retrieve a small batch of documents to avoid out-of-memory errors
      ApiFuture<QuerySnapshot> future = collection.limit(batchSize).get();
      int deleted = 0;
      // future.get() blocks on document retrieval
      List<QueryDocumentSnapshot> documents = future.get().getDocuments();
      for (QueryDocumentSnapshot document : documents) {
        document.getReference().delete();
        ++deleted;
      }
      if (deleted >= batchSize) {
        // retrieve and delete another batch
        deleteCollection(collection, batchSize);
      }
    } catch (Exception e) {
      System.err.println("Error deleting collection : " + e.getMessage());
    }
  }

For the entire file, including imports, see this Github Gist .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM