0

So I have an web app written with flutter, and recently I added security rules on Firestore DB, but I cannot find any documentation on how to test these security rules with flutter test. I also want to do this test against the Firebase Emulator, which I setup.

I found lots of examples to test it with node and npm, like HERE, but I don't use JS, I wrote everything within Flutter.

I have tried different scenarios which mostly failed:

  1. Mocking Firebase.initializeApp() with this solution but since it's mocked I cannot use any implementation for FirebaseFirestore.instance or FirebaseAuth.instance.

Sample code:

Future<void> main() async {
    setupFirebaseAuthMocks();

    await Firebase.initializeApp();
    final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;

    firebaseFirestore.useFirestoreEmulator(
      EMULATOR_HOST,
      FIREBASE_PORT,
      sslEnabled: false,
    );

    final CollectionReference colUserRef =
        firebaseFirestore.collection('users');

    BaseUser user = BaseUser(
      uid: 'uid',
      email: 'email',
      createdAt: FieldValue.serverTimestamp(),
      updatedAt: FieldValue.serverTimestamp(),
    );

    test('Check if', () async {
      await firebaseFirestore.collection('users').doc().set(user.toJson());
      print('works?');
    });
}

Error I got:

package:flutter/src/services/platform_channel.dart 294:7 MethodChannel._invokeMethod
MissingPluginException(No implementation found for method DocumentReference#set on channel plugins.flutter.io/firebase_firestore)
  1. I tried to use FakeFirebaseFirestore() but this class doesn't have useFirestoreEmulator()

Sample code:

import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
...
FirestoreService firestoreService = FirestoreService.mock(firestoreInstance: FakeFirebaseFirestore());

firestoreService.firebaseFirestore.useFirestoreEmulator('127.0.0.1', 8081);
...

Error I got:

Class 'FakeFirebaseFirestore' has no instance method 'useFirestoreEmulator' with matching arguments.
Receiver: Instance of 'FakeFirebaseFirestore'

Solution that I found to finally work!

I just created a fakeApp() which I use with the integration tests that Flutter Web has HERE. Since I have to initialize Firebase and my requirement is to use Firebase Emulator within a pipeline.

Sample Code:

// Simple and bare bone flutter app
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(context) => const Center(
          child: Text('Firebase Security Rules Test!'));
}

Future<void> fakeApp() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  FirebaseAuth.instanceFor(app: Firebase.app());
  runApp(const MyApp());
}

// Tests implementation
Future<void> main() async {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  late FirebaseFirestore firebaseFirestore;
  late FirebaseAuth firebaseAuth;
  late CollectionReference colUserRef;
  late BaseUser user;

  setUpAll(() async {
    await fakeApp();
    firebaseFirestore = FirebaseFirestore.instance;
    firebaseAuth = FirebaseAuth.instance;

    firebaseAuth.useAuthEmulator(
      EMULATOR_HOST,
      FIREBASE_AUTH_PORT,
    );

    firebaseFirestore.useFirestoreEmulator(
      EMULATOR_HOST,
      FIREBASE_PORT,
      sslEnabled: false,
    );

    colUserRef = firebaseFirestore.collection('users');

    user = BaseUser(
      uid: '1234567890',
      email: 'email@email.com',
      createdAt: FieldValue.serverTimestamp(),
      updatedAt: FieldValue.serverTimestamp(),
    );
  });
  group('Firestore Security Rules Tests -', () {

    group('/users/* collection Tests-', () {

      group('Unauthenticated Tests -', () {

        /*
        * Test if Unauthenticated User can list all users collection from firestore
        * Should give permission-denied
        * Rule LIST
        */
        testWidgets('U: list users/*', (WidgetTester tester) async {
          late String eCode;

          try {
            await colUserRef.get();
            eCode = 'allowed';
          } on FirebaseException catch (e) {
            eCode = e.code;
          }

          expect(eCode, 'permission-denied');
        });
      });
    });
  });
}

Flutter Drive command that I use:

flutter drive \
--driver=test_driver/integration_test.dart \
--target=integration_test/sec_rules_test.dart \
--device-id web-server \
--dart-define=PROJECT_ID=someProjectId

Also by using IntegrationTestWidgetsFlutterBinding.ensureInitialized() I make sure:

  • I get my tests status logs
  • I get the fakeApp() to be close automatically when the tests finish.
  • If I don't use IntegrationTestWidgetsFlutterBinding, and use with web-server I get no logs.
  • If I don't use IntegrationTestWidgetsFlutterBinding, and use with chrome I get logs but the tests are executed twice.
  • If I don't use flutter drive for my tests, and use flutter test, I get Web devices are not supported for integration tests yet.

So basically I use flutter web integration tests for this to work.

What do you think about this approach? Should I go back and use a more mature security rules unit test with node? Do I have other possibilities to unit test my security rules?

I was thinking also to test these rules with restAPI calls from here and here, I think these are not within admin sdk context so they behave the same.

Do you see, maybe, something that I don't see within the possibility of these unit tests? Anyhow, hope this helps other in my situation.

Xao
  • 512
  • 6
  • 17

0 Answers0