Testing in Flutter

It’s clear that Flutter is a way to create beautiful UI cross platform, and the developer experience is lauded my many (not just me). But what about the not so pretty things … TESTING!!

Some people love testing apps, I haven’t met any myself, but I hear some do. I personally don’t mind unit testing, especially in Android as Junit and Mockito are just easy to use. UI testings, urgh, espresso is painful, and most companies seem to want a cross platform solution like Appium. So lets take a look at unit testing, some widget testing, and finally UI testing on a Flutter project…

Unit Testing

With unit testing, I found it very similar to Junit and Mockito. It uses the flutter_test library, and the mockito library.

A unit test file could look something like this:

  • Each test class has to be named your_class_name_test.dart this will ensure that they are run when executing flutter test
  • setUp will execute before each test inside the file
  • setUpAll will execute once before all tests
  • tearDown will execute after each test inside the file
  • tearDownAll will execute once after all tests
  • test can be synchronous or asynchronous depending on what is needed.

Mocking and Assertions

As Dart doesn’t have reflection (well Flutter Dart doesn’t), it’s not quite as easy as in Java / Kotlin world to make mocks. But thanks to the mockito library and build_runner it’s also not hard.

Simply just add an annotation for @GenerateMocks and list the classes you want to mock. After you’ve added them run the following command

flutter pub run build_runner build

This will generate mock classes for you. So now MockFirebaseAuth and MockUser exist, and can be used inside tests.

So lets look at some simple mocking:

As you can see we pass our MockFirebaseAuth into the MyAuth constructor. Now we’re able to control what a function returns, verify that functions were called. We’re also able to use argument matchers and captures, full details can be found here https://pub.dev/packages/mockito

Assertions

Flutter uses expect(actual, matcher) so in the example above we expect the user returned from getCurrentUser to be equal to mockUser as that is what we returned from MockFirebaseAuth.

There are many matchers that can be used like:

  • hasLength — for checking a lists length for example
  • containsValue — for checking a map has a specific value
  • isNull — for checking a value is null

many many more can be found here https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html

Integration Testing / Widget Testing

As everything in Flutter is a Widget, Widget testing means we can test UI logic without the need for a device / emulator. We can test an individual component, a page, or even the entire app (as that too is a widget).

The test file looks very similar, and can be inside the same file as general unit tests if you wanted:

Instead of using test we use testWidgets which provides a WidgetTester. So lets create a simple page, and try and test a few things:

A simple page, with an AppBar and an ElevatedButton . When the button is pressed we set the pageTitle triggering a rebuild and that String to be used in the AppBar

  • The first test adds the MainPage to an app, so it can be tested. Then searches for “Not set” and expects to find this once in the page
  • The second test adds the MainPage widget again. This time taps the ElevatedButton . Now it calls tester.pump() to make sure that the state change takes effect, and then expects to find “Hello World” once in the page.

These are very simple tests, but you can test a lot with Widget Tests and gain a lot of test confidence in the app.

More information on widget testing can be found here https://docs.flutter.dev/cookbook/testing/widget/introduction.

UI Testing

Very simply, just do the same as the Widget Tests but create the files in the integration_test folder. Instead of starting a widget, start the app itself…

In this test I’m testing one of my own Flutter apps. Clicking on “Host a session” should show a login bottom sheet…

  • We start the app
  • Use pumpAndSettle to make sure that all animations have finished
  • Tap on the “Host a session” button
  • Expect a Widget with type LoginBottomSheetContent to be visible

Appium

At the moment, Appium doesn’t support Flutter apps itself. However, there does seem to be support from the community. Browserstack have a page advising how to use this here https://www.browserstack.com/guide/test-flutter-apps-with-appium

As I’m not an Appium developer, I’m not going to attempt this.

Again, it appears that Flutter have developed with hindsight. Unit testing is well thought out, Widget testing is very powerful, and UI testing also just as good.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store