With so many challenges I am not surprised that people do not use Flutter Driver Test so often.

I really believe that if they were resolved, many more people would start using those

…so let’s fix them!

Fixing: Hard to debug failing test

You cannot debug an application when the test is running, but you can repeat the same steps of the test when you are debugging the main.dart file.

If a test in main_test.dart fails (eg HTTP request is not properly mocked), then just start the main.dart as a normal application in the debug mode.

First, you need to add -t (or — target ) parameter in the IntelliJ settings:

Then press the debug button — at this point just reproduce your test’s steps.

IMPORTANT: If you need to enter manually text into TextFields , remember to comment out enableFlutterDriverExtension from main.dart .

Fixing: They require a real device to run on

Now Flutter Desktop gets handy — you can build your application for Windows/macOS/Linux so why not running UI tests against desktop as well.

You won’t be able to run every (testing device-specific components like WebView Widget ), but from my experience, those tests are around 5% of all the Flutter Driver Tests.

Run main.dart file as a normal desktop application:

flutter run -d windows

After the application launches you will see the message:

An Observatory debugger and profiler on Windows is available at: http://127.0.0.1:65521/vBWe3pt2_0k=/

The URL is here important as the tests require it to connect to the application

Run the main_test_dart and pass that link to it:

dart main_test.dart http://127.0.0.1:65521/vBWe3pt2_0k=/

dart command can run any file that has main() function.

Within the main_test.dart file you need to extract that link form args that you receive in main(List<String> args) and pass it as dartVmServiceUrl to FlutterDriver.connect .

And done — the test is running on your desktop! Or even docker!

Fixing: Running one test file at a time

From the previous fix we’ve learned that we need to do three things to run a single file:

Run main.dart with flutter run command Wait for the app to start and copy the Dart VM URL Run main_test.dart with given URL

To run multiple files you just need a script that:

Looks up all the files ending with _test.dart in a given folder that are the tests files

in a given folder that are the tests files Looks up files that have the same name as the tests but without _test.dart — those are the application files

— those are the application files Runs flutter run command for each application file sequentially and observe the console output for is available at: (http://.*/) match and copy it

match and copy it Runs dart <test file> <dart vm url> on each file

Now you have automated running multiple test files.

Fixing: They are much slower than Widget Tests

At this point, the test from a single file runs quite quickly but when we start each test file we need to recompile the application and reconnect to the Dart VM which can take an additional 20 seconds per file.

To improve this we simply should leverage Flutter hot restart.

This includes two things:

Communicate from main_test.dart that main.dart should perform a hot restart

This can be achieved with FlutterDriver::requestData method that sends a message to main.dart . Inside main.dart you will receive this message in enableFlutterDriverExtension handler:

enableFlutterDriverExtension(

handler: (request) async {

// Here will be the message

},

);

Perform hot restart on main.dart side

To do that, you need to wrap your main application in a widget that will reinsert your application’s widget into the widget tree, I use streams for the communication:

Done. Now we need to run all our tests from a single _test.dart file:

import ‘main1_test.dart’ as main_file_1;

import ‘main2_test.dart’ as main_file_2;

import ‘main3_test.dart’ as main_file_3; void main(List<String> args) {

main_file_1.main(args);

main_file_2.main(args);

main_file_3.main(args);

}

Fixing: It’s hard to inject different setup from test file into the application file

Now when we can communicate with the app we need to send serialized configuration using FlutterDriver::requestData method.

I would recommend having a configuration class that can be passed to your MyApplication widget.

For me, this config contains the initial route, page’s arguments or even it specifies if the application should fake or real HTTP requests.

Fixed: Each test in _test file depends on the previous test

With the ability to send a restart request with a specific configuration, we can restart the application between each test:

setUp(() async {

await restart(

driver,

config: const Configuration(

route: Routes.curves,

repeatAnimations: false,

),

);

}); test(‘shows curves’, () async {

await driver.waitFor(find.byType(‘CurvesPage’));

}); test(‘scroll’, () async {

await driver.scroll(find.byType(‘CurvesPage’), 0, -400,

const Duration(milliseconds: 100));

await driver.waitFor(find.byType(‘CurvesSection’));

}, retry: 1);

With this change, every test has a fresh start.

If your tests are flaky, you can now add retry property to rerun tests if it fails.

Final Result

Here you can find an example application and run Flutter Driver Tests.