TLDR: mazei513's comment is right. As Flutter Firestore is a plugin and not a package, it needs a platform to run on. This platform could be an iOS phone or an Android virtual device for instance. Therefore, you can't write a unit test to test a Firestore command. You have to use the integration_test package to execute this kind of tests.
Understanding the difference between plugin vs package
As stated by the doc, a plugin
is a special kind of package that makes platform functionality
available to the app. Plugin packages can be written for Android
(using Kotlin or Java), iOS (using Swift or Objective-C), web, macOS,
Windows, Linux, or any combination thereof.
If you take a look at the Firestore plugin in /usr/local/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.7.0
, you'll see:
jjanvier:nouba$ ls -l /usr/local/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.7.0
total 48
drwxr-xr-x 4 jjanvier jjanvier 4096 Oct 6 17:20 android
drwxr-xr-x 3 jjanvier jjanvier 4096 Oct 6 17:20 ios
drwxr-xr-x 3 jjanvier jjanvier 4096 Oct 6 17:20 lib
drwxr-xr-x 3 jjanvier jjanvier 4096 Oct 6 17:20 macos
-rw-r--r-- 1 jjanvier jjanvier 912 Oct 6 17:20 pubspec.yaml
As Firebase plugins actually run native code depending on the platform (Android, iOS, MacOS...), they need a "real" device to run on. Therefore, it doesn't work with flutter test
that is nothing but a simple Dart executor.
Testing a Firestore repository (or a command or a query)
About the device emulator
To get a platform your tests can run on, you have several options. For instance:
- boot an Android Virtual Device from your IDE. That's what I do when I work locally.
- setup an Android Virtual Device on your continuous integration pipeline. For instance you this one for Github actions. Then you can start the emulator from the command line.
- use Firebase Test Lab. I didn't use it, but it should work.
Using integration_test
Follow this documentation to configure integration_test
for your project. Don't forget to use testWidgets
instead of test
as this what will allow to launch your tests in the device emulator instead of in the Dart simple environment.
Once done, all the integration/end to end tests can be launched with flutter test integration_test
.
Do not mess your production database
If you launch the command flutter test integration_test
as is, there is a high chance the document {'name':'Dev','title':'Dev'}
present in your test will appear in your production database.
To counteract this problem, you have mainly 2 options at your disposal:
- setup a real Firebase project that you'll use for your tests
- use the Firebase local emulator suite
For my project, I chose the second option. With a single command, you can boot the Firebase emulator, loads fixtures and run the tests :
firebase --config="firebase.test.json" emulators:exec --import fixtures/ "flutter test integration_test/ --dart-define=FOO=bar"
Let's detail the command:
--config="firebase.test.json"
launch firebase with this config file. This allows me to keep my local emulator suite running while I launch the tests
--import fixtures/
: loads the folder "fixtures" as dataset
emulators:exec "flutter test integration_test/ --dart-define=FOO=bar"
: launch the integration tests with a FOO env var
About mocking/faking Firestore
I didn't suggest you to mock or fake Firestore as it makes no sense in your case. There is no point in using a double for Firestore if what you want to test is that you can actually write properly in Firestore.
But, in some cases it can be useful. For instance to unit test a business behavior that at some point writes or queries Firestore.
Again, you have several solutions:
- you can mock Firestore by yourself, like it's done here. But it's quite complex and painful to do IMO.
- you can use the package fake_cloud_firestore. As the name suggest, it's a complete Cloud Firestore fake. So far, so good, it works perfectly for me.