/autopilot

A test driver for Flutter to do QA testing without sharing app source code.

Primary LanguageDartMIT LicenseMIT

Autopilot

A test driver for Flutter to do QA testing without sharing app source code. It exposes a JSON API using an HTTP server running inside the app. Using these APIs you can write tests in any language for your Flutter app.

Getting started

Add package to dependencies:

flutter pub add autopilot

Create main_test.dart along side of your main.dart file. Make AutoPilot widget parent of your MaterialApp or root widget like below:

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

import 'my_app.dart';

void main() {
  runApp(
    Autopilot(child: MyApp())
  );
}

Run your app on device/emulator:

flutter run --release --target lib/main_test.dart

On Android forward port 8080 so that you can access it via localhost:

adb forward tcp:8080 tcp:8080

Consider following example:

Text(
  "Hello World!",
  key: Key("txtGreet"),
)

Example of a test in python using pytest:

# example_test.py
import requests

root = "http://localhost:8080"

def get(path):
    return requests.get(root + path).json()

def test_greet():
    greet = get("/texts?key=txtGreet")[0]
    assert greet["text"] == "Hello World!"

Run it:

python -m pytest example_test.py

Inspiration

Flutter has a really amazing testing suite for Unit, UI and Integration testing. But one problem is that you need to know/learn Dart and you have to share the source code of the app to the person who writes tests. This doesn't work in every work environments.

But Flutter framework is so transparent I was able to tap into its internals and build a JSON API which can provide pretty much everything you need to write UI automation tests.

APIs

GET /widgets

Returns entire widget tree

GET /keys

Returns list of all the keyed widgets

GET /texts

Returns list of all text widgets

GET /texts?text=<text>

Returns list of all text widgets with matching text

GET /texts?key=<key>

Returns text widget that matches key

GET /editables

Returns list of all text fields

GET /type?text=<text>

Types given text to the focused text field

GET /tap?x=<x>&y=<y>

Taps at given offset

GET /tap?key=<key>

Taps on widget with given key

GET /tap?text=<text>

Taps on text widget with given text

GET /hold?x=<x>&y=<y>

Tap and hold on given offset

GET /drag?x=<x>&y=<y>&dx=<dx>&dy=<dy>

Taps at (x,y) and drags (dx, dy) offset

GET /scroll?key=<key of scrollable>&dx=<dx>&dy=<dy>

Taps inside scrollable with given key and drags (dx, dy) offset

GET /scroll-into?scrollable-key=<key of scrollable>&key=<key of widget>&dx=<dx>&dy=<dy>&delay=<delay between drags in ms>&timeout=<timeout in ms>

Taps inside widget with given scrollable-key and drags (dx, dy) offset until given key is present in the widget tree. If the widget is inside a scrollable it will be made visible.

After one drag action it will pause for delay milliseconds and then check for the key. Default value is 500 ms.

Operation times out after timeout milliseconds. Default value is 5000 ms.

GET /screenshot

Returns screenshot of app in PNG

GET /keyboard

Shows keyboard

DELETE /keyboard

Hides keyboard

POST /keyboard?type=<type>

Submits a keyboard action.

Some actions may not be available on all platforms. See TextInputAction for more information.