如何从 Dart VM/Dart 集成测试访问 Firebase Firestore?

And*_*ena 5 dart firebase flutter google-cloud-firestore

我正在编写一个 Flutter 应用程序,该应用程序在 Firebase Firestore 文档上具有相当复杂的逻辑。我正在尝试使用flutter_test实际针对数据库执行此逻辑来编写单元测试(我知道这在技术上是一个集成测试)。这是因为这个逻辑有很多边缘情况,我只能确定在针对真实数据库进行测试时可以正常工作。

这似乎是一项不可能完成的任务。

  1. 由于身份验证的实现方式,cloud_firestore包只能在手机内部运行。
  2. firebase包有 2 个实现”。一种只能在浏览器上运行,另一种是 Dart VM,它是 REST API 的低级包装器,几乎完全没有文档记录。

2. Dart VM 的firebase包有以下示例:

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);

  ...
}
Run Code Online (Sandbox Code Playgroud)

...但是它没有显示如何获取credential. googleapis包显示如何获取credentials

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

...但是这个对象不是字符串,并且没有在任何地方编写如何将其转换为FirebaseClient类期望的内容(toString()不起作用)。firebase 包上有一个关于如何获取此凭据的Github 问题,但仍未得到解答。

我很难相信我在网上找不到关于如何为 Firebase Firestore 编写正确集成测试的信息。

注意事项:

  • 我没有兴趣嘲笑 Firestore,因为我的逻辑很复杂,并且我想针对真实数据库测试每个边缘情况。
  • 我没有兴趣使用Flutter Driver,因为测试像手机中的普通应用程序一样安装,并且在开发过程中需要时间,而且调试起来不像常规单元测试那么简单。Android Studio有非常好的测试调试工具。

我应该如何编写访问 Firebase Firestore 的集成测试?

Ala*_*n M 4

对于像我几个小时前一样被带到这里的其他人,通过搜索在将credentialdart VM 与 Firebase 结合使用时创建该神秘对象的方法,以下是我的解决方案,至少适用于访问 Firestore。

这项工作是由 Credentials 类完成的:(下面实现)

import 'package:firebase/firebase_io.dart';

String credential = await Credentials.fetch();
FirebaseClient fbClient = new FirebaseClient(credential);
Run Code Online (Sandbox Code Playgroud)

Credentials 类使用 Firebase 服务帐户私钥中的数据创建凭据(Firebase 控制台 > [项目] > 设置 > 项目设置 > 服务帐户,然后下载私钥)。

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);
  }
}
Run Code Online (Sandbox Code Playgroud)