All Articles

Flutter Web Integration Tests Run Twice

Flutter Testing Web

There’s a bug with flutter drive -d chrome. When you run this command, the test executes twice. Once as a background process and once in the foreground. As a developer, you don’t notice the background process. I only noticed it because the test was behaving strangely - for example, my test creates a test account and depending on timing, the account already existed.

The issue already exists on GitHub, but so far there was no satisfying solution for me.

I’ve now found a workaround that meets all my requirements:

void main() {
  if (kIsWeb && html.window.navigator.userAgent.contains('HeadlessChrome')) {
    return;
  }
  // Tests here
}

The idea is to terminate the process that starts in the background right at the beginning. To distinguish which process is running, I check the UserAgent for ‘HeadlessChrome’. If it contains it, I can simply terminate it.

The other solutions and why they didn’t work for me

-d web-server instead of -d chrome

flutter drive -d web-server --browser-name chrome --no-headless

This is the most commonly suggested solution. It works – only one Chrome instance starts. But: There are no logs in the terminal. All print() statements and error outputs disappear. Useless for debugging.

Tests on Android/Desktop instead of Web

Some teams work around the problem by running their integration tests on Android or Desktop. This works, but doesn’t test web-specific behavior. Some packages (e.g., Riverpod) behave differently on web – a green desktop test doesn’t guarantee a working web app.

Firestore flag as synchronization

A creative workaround: Use a Firestore document as a flag to detect which instance was first. The second instance then waits until the first one is done. This works, but is extremely complex and requires Firestore setup just for this workaround.

My solution: Detect HeadlessChrome

My approach is simpler: The second instance runs as HeadlessChrome. This is in the user agent. A check at the beginning of the tests terminates it immediately:

if (kIsWeb && html.window.navigator.userAgent.contains('HeadlessChrome')) {
  return;
}
  • No change to the command needed
  • Logs are preserved
  • No external setup required
  • Works with existing CI/CD pipelines
Till Friebe

About Till Friebe

Part of the FriebeDev team - four siblings from Berlin who develop Flutter apps together. With over 300,000 downloads and projects for companies like get2Card and BlazeSQL.

More about our team →

Ready for Your App?

Let's discuss your project in a free initial consultation.

Free Consultation