Pull to refresh

Unlocking Selenium Testing for Flutter's Web Apps

Level of difficultyMedium
Reading time3 min
Views235

Hello everyone. I've decided to share a solution to a problem that often leaves newcomers baffled and veterans sighing when it comes to UI testing of web applications built with Flutter. Essentially, it comes us to forget about all the powerful tools like Selenium and Protractor.

For those who are not familiar with Flutter, let me explain the challenge in simple terms.

Flutter doesn't generate standard HTML elements that are easily targeted by testing tools. Instead, it simply draws the UI. What you get is essentially a canvas with some wrapping around it. Without further ado, take a look at this site and see for yourself: https://flutter-gallery-archive.web.app/

Here's what the flutter-view looks like:

In other words, there's nothing to latch onto.

Searching doesn't lead to anything useful. Or more precisely, it leads to Flutter's own testing tools: integration_test. This is a powerful tool. You can run tests for any platform you're building your app for. In essence, you're compiling a special version of the application for testing purposes. Need to change a test? Compile again. You simply don't have the ability to test a deployed web application. It's a kind of gray-box testing. Moreover, the learning curve is steep.

The manual testers, who would like to learn automation, will have to get to grips with Flutter and learn how to run it in an IDE. It's all doable, but more complex compared to starting with standard tools like Selenium testing. Running a small test leads to compiling the project, and that takes time.

Enough about the problems. Let's move on to the solution for full-fledged black-box testing.

We'll be using Accessibility features, which are used, for example, by screen readers. Web users of screen readers must toggle the "Enable accessibility" button to build the semantics tree. You can skip this step if you programmatically auto-enable accessibility for your app using this API:

import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

void main() {
  runApp(const MyApp());
  SemanticsBinding.instance.ensureSemantics();
}

Flutter's standard widgets generate an accessibility tree automatically. So, the only need is to populate the DOM tree with UI annotations. In the case of black-box testing, if the accessibility tree is not built, that’s not a problem. You can click a 1 pixel button to do that.

Here's how to do it manually:

  1. Open your Flutter web application in a browser (we use this one as an example https://flutter-gallery-archive.web.app/)

  2. Open the Developer Tools (you can do this by right-clicking on the page and selecting "Inspect" or by pressing Ctrl+Shift+I on Windows/Linux or Cmd+Option+I on macOS).

  3. Go to the "Console" tab within the Developer Tools.

  4. Paste and execute the following script:

document.querySelector('flt-glass-pane').shadowRoot.querySelector('flt-semantics-placeholder').click();

The DOM is populated, and you can execute tests.

There's a nuance with input fields. Before entering data, you need to activate them by sending a focus command. In some cases, a click will work. However, these are nuances that can be overcome.

Nevertheless, you can write standard tests. Here's an example with Selenium:

// Assert
const clickMeButtonXPath = '//flt-semantics[contains(@aria-label, "Click Me")]';
let clickMeButton = await driver.wait(until.elementLocated(By.xpath(clickMeButtonXPath)), 30000);

clickMeButton.click();

const clickedMeLabelXPath = '//flt-semantics[contains(@aria-label, "You clicked me")]';
let clickedMeLabel = await driver.wait(until.elementLocated(By.xpath(clickedMeLabelXPath)), 30000);

expect(clickedMeLabel).toBeDefined();

You should rely on the aria-label attribute to be used in your locators. You can explicitly manage this attribute it through Semantics widgets. Recently, in Flutter version 3.19.0, the identifier property has been added. But unfortunately, it doesn't work for the Web.

I've written a small npm package as an example, and it facilitates quick start. You can find code here https://github.com/rentready/flutter-selenium-bridge/. I'm not sure whether it will work for everyone. So, if there are any additions - welcome to contribute. 

Tags:
Hubs:
+3
Comments0

Articles