Andre Pena
Andre Pena

Reputation: 59456

How do I access Firebase Firestore from Dart VM / Dart integration tests?

I am writing a Flutter app that has fairly complex logic over the Firebase Firestore documents. I am trying to write unit tests using flutter_test that actually execute this logic against the database (I know this is technically an integration test). This is because this logic has a lot of edge-cases I can only be sure are working if tested against the real database.

This seems to be an impossible task.

  1. The cloud_firestore package can only run inside the phone because of how the authentication was implemented.
  2. The firebase package has 2 "implementations". One can only work on the browser, and the other one, the Dart VM one, which is a low level wrapper around the REST API, is almost completely undocumented.

The 2. firebase package for Dart VM has this example:

import 'package:firebase/firebase_io.dart';

void main() {
  var credential = ... // Retrieve auth credential
  var fbClient = new FirebaseClient(credential); // FirebaseClient.anonymous() is also available

  var path = ... // Full path to your database location with .json appended

  // GET
  var response = await fbClient.get(path);

  // DELETE
  await fbClient.delete(path);

  ...
}

... however it does not show how to get the credential. The googleapis package shows how to get the credentials:

final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
  "private_key_id": ...,
  "private_key": ...,
  "client_email": ...,
  "client_id": ...,
  "type": "service_account"
}
''');

... however this object is not a string and it is not written anywhere how to transform this into what the FirebaseClient class expects (toString() does not work). There is a Github issue on the firebase package on how to get this credentials but it is still unanswered.

I find it hard to believe that there is no information available online, that I could find, on how to write proper integration tests for Firebase Firestore.

Considerations:

How should I write integration tests that access Firebase Firestore?

Upvotes: 5

Views: 661

Answers (2)

atreeon
atreeon

Reputation: 24197

Adding the scope below allowed me to access firebase realtime

'https://www.googleapis.com/auth/userinfo.email'

According to https://developers.google.com/identity/protocols/oauth2/scopes it says that that scope is allowed to 'View your email address'. This doesn't seem quite right but it made it work.

(if anyone can help me understand this please add to the answer).

Upvotes: 0

Alan M
Alan M

Reputation: 98

For others who were brought here, as I was a few hours ago, by a search for ways to create that mysterious credential object when using the dart VM with Firebase, the following is my solution which works for accessing Firestore, at least.

The work is done by the Credentials class: (implemented below)

import 'package:firebase/firebase_io.dart';

String credential = await Credentials.fetch();
FirebaseClient fbClient = new FirebaseClient(credential);

The Credentials class creates the credential, using data from the Firebase Service Account private key (Firebase Console > [Project] > Settings > Project Settings > Service Accounts, then download the private key).

import 'dart:io';
import 'dart:convert';
import "package:googleapis_auth/auth_io.dart";
import 'package:http/http.dart' as http;

class Credentials {

  /// Returns a credential string to be used in the constructor
  /// of [FirebaseClient].
  static Future<String> fetch() async {
    Map<String, dynamic> pk = await getPrivateKey();
    // Fields from Firebase private key
    var accountCredentials = ServiceAccountCredentials.fromJson({
      "private_key_id": pk['private_key_id'],
      "private_key": pk['private_key'],
      "client_email": pk['client_email'],
      "client_id": pk['client_id'],
      "type": "service_account",
    });

    // Define the required scopes.
    // see https://firebase.google.com/docs/firestore/use-rest-api#working_with_google_identity_oauth_20_tokens
    var scopes = [
      "https://www.googleapis.com/auth/datastore",
    ];

    var client = new http.Client();
    AccessCredentials credentials =
        await obtainAccessCredentialsViaServiceAccount(
            accountCredentials, scopes, client);
    client.close();
    return credentials.accessToken.data;
  }

  static Future<Map<String, dynamic>> getPrivateKey() async {
    String jsonString =
        await File('/Path/to/firebase_private_key/keyfile.json')
            .readAsString();
    return json.decode(jsonString);
  }
}

Upvotes: 4

Related Questions