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:
- Mocking
Firebase.initializeApp()
with this solution but since it's mocked I cannot use any implementation forFirebaseFirestore.instance
orFirebaseAuth.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)
- I tried to use
FakeFirebaseFirestore()
but this class doesn't haveuseFirestoreEmulator()
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 withweb-server
I get no logs. - If I don't use
IntegrationTestWidgetsFlutterBinding
, and use withchrome
I get logs but the tests are executed twice. - If I don't use
flutter drive
for my tests, and useflutter test
, I getWeb 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.