Flutter testing: Speeding up unit & widget test run on CI

Akhmat Sultanov
3 min readFeb 17, 2023

--

Hey, Medium. For the last couple of months, I have been working as an SDET specialist using Flutter and Gitlab CI to monitor code quality and tests. Now we have over 1000 unit and widget tests.
At some point, a test run on our CI started taking over 40 minutes.
After researching, I found some excellent tricks that helped reduce a run time to 2 minutes.

1. Use only the necessary arguments in the test run command that are appropriate for the task.

❯ flutter test
00:14 +94: All tests passed!
❯ flutter test --no-pub --coverage
04:30 +94: All tests passed!

Therefore, I left only the flutter test command without additional arguments to run on ci. After all, other tasks take extra time, and on commit checks, every second is precious.

2. You need to realize call all tests from one test file.

In Flutter, each file that has tests takes time to run because it has to compile, start a new process, etc. So it can take 3–7 seconds just for each run.

Therefore, you can resort to this trick:

import test_1_test.dart as test_1
import test_2_test.dart as test_2

void main() {
test_1.main();
test_2.main();
}

With this structure, I can run all tests or any group of test files in the same amount of time as one test file.

3. Analyze the run speed for each test separately.

I’ve encountered unpredictable test behavior, and I’ve seen that during a run, multiple tests take longer to run than the entire suite put together.

flutter test -v > test_output.txt
With this command, you can view detailed test output. In addition, it should store timestamps for each test. This can be a good start for figuring out which tests are slow and worth rewriting.

4. You can also disable checkIntrinsicSizes to speed up the build process of the widget test.

Create a test binding (subclass of AutomoatedTestWidgetsFlutterBinding) that returns false for “checkIntrinsicSizes” and instantiate it before calling testWidgets for the first time. This disables an expensive test that checks for internal contract violations.

class AutomatedTestWidgetsFlutterBindingWithCheckIntrinsicSizesDisable extends AutomatedTestWidgetsFlutterBinding { 
@override bool get checkIntrinsicSizes => false;
}

void main() {
AutomatedTestWidgetsFlutterBindingWithCheckIntrinsicSizesDisabled();
testWidgets('do something', (tester) async {});
}

5. Use parallelization to run tests.

This has some advantages. For example, you can use — total-shards and — shard-index to parallelize tests in CI (by creating multiple jobs to run each separate test subset)

6. Use a docker container that suits your needs.

Working with a heavy docker container takes more time than a lighter one. For example, you can see the Android SDK in public docker images, but it is only needed to run on Android.
Unit and widget tests do not require this SDK, so you can run tests without it and use only the Flutter SDK.

I wish you all stable and fast test runs.

--

--

Akhmat Sultanov

SDET with experience in Mobile testing (iOS and Android, cross-platform and native automation), API testing, and CI/CD. https://www.linkedin.com/in/akhmat-s/