Multi-Select Dialogue in Flutter: The Unit Test Phase

Zujaj Misbah Khan
Level Up Coding
Published in
10 min readMar 23, 2021

--

Multi-Select Dialogue in Flutter: The Unit Test Phase
Multi-Select Dialogue in Flutter: The Unit Test Phase

· OVERVIEW
· WHY TEST?
· UNIT TEST
· THE TESTING PHASE
· PREPARE FOR TAKEOFF
· LIST TEST CASE
CASE 1: LIST IS NULL
CASE 2: LIST IS EMPTY
CASE 3: LIST IS NOT EMPTY
CASE 4: LIST HAS TOTAL FOUR FLAVOURS
CASE 5: THE FIRST FLAVOUR OF LIST IS CHOCOLATE
CASE 6: THE LAST FLAVOUR OF LIST IS PEANUT BUTTER
CASE 7: LIST HAS TWO FLAVOURS STARTING WITH THE LETTER C
CASE 8: LIST HAS TOTAL FIFTEEN VOWELS
CASE 9: LIST HAS TOTAL OF TWENTY CONSONANTS
CASE 10: LISTS ARE NOT SAME
· MAP TEST CASES
CASE 1: MAP IS NOT NULL
CASE 2: MAP IS EMPTY
CASE 3: MAP KEY IS A STRING
CASE 4: MAP VALUE IS A BOOL
· FUNCTION TEST CASES
CASE 1: convertToMap REQUIRES A LIST PARAMETER
CASE 2: convertToMap RETURN TYPE IS A MAP
CASE 3: mappedFlavour SHOULD NOT BE EMPTY
· POST CONVERSION TEST CASES
CASE 1: mappedFlavour HAS NO KEY CHERRY
CASE 2: mappedFlavour HAS NO VALUE 0
CASE 3: mappedFlavour HAS NO BLANK ENTRIES
· FINAL RESULTS
· WAIT THERE’S MORE?
· REFERENCES

OVERVIEW

Are you lost in searching for ways to test an application in flutter? Do you fear the word “test”? Have you got no clue where to start? Are you looking for answers that would give you a birds eye’s view? Fear no more, you landed on the right spot.

Testing an application can be quite a mind streaming job. Not only it requires massive time but also your background programming experiences. From now on we will be exploring how to go through these testing phases one by one.

After a successful contribution to the Level Up Coding platform, it gives me the joy to learn and explore more about the published article entitledMulti-Select Dialogue in Flutter: A Versatile Approach. This article is a focused series that targets teaching about different testing approaches used in Flutter.

WHY TEST?

You might be wondering why do we even need to write tests for an application? What satisfaction do I get after testing? Is testing really that fruitful for me? Following are some major benefits¹ that might answer your queries.

  • It helps to clean bugs inside your application.
  • It raises your level of confidence with the client.
  • It prevents the application from unexpected crashing behaviour.
  • As a developer, your code gets polished from time to time.
  • Security & performance loopholes are identified before moving towards the production phase.

According to global standards, we usually follow a testing paradigm² to chunk up the test phases. So mapping it in terms of flutter, we can say that testing can be broken into three phases, see figure 1.

  • Unit Tests.
  • Widget Tests.
  • Integration Tests, also known as End to End Tests (E2E).
The Flutter Testing Paradigm
Figure 1: Flutter Testing Paradigm

We will cover these terminologies from time to time, just keep them in mind.

UNIT TEST

The term “ unit ’’ can be referred to as the smallest logical code fragment that acts as a base building block for an application. The scope of a unit test can vary from a single line of code to a huge collection of class objects. Ham Vocke clearly describes the concept of unit tests in his statement as below.

If you’re working in a functional language a unit will most likely be a single function. Your unit tests will call a function with different parameters and ensure that it returns the expected values. In an object-oriented language a unit can range from a single method to an entire class.

The Practical Test Pyramid

THE TESTING PHASE

Let’s take the example of the Multi-Select Dialogue app that was made by the developer Mr X. After a keen examination of the application code, we conclude a theory that the smallest unit of this app is the list of flavours that are being manipulated. In terms of abstraction, each action that would be performed throughout the app depends upon how the flavours are represented.

Figure 2: Illustration of List<String> flavour
Figure 2: Illustration of List<String> flavour.
List<String> flavours Declaration

Initially, it would look just like a plain list of flavours but if you look closely when the dialogue pops up these flavours, this list gets transformed into a map that not only holds the flavour values but also it’s corresponding boolean value as shown in figure 3.

Figure 3: Illustration of List<String> flavours transformation
Figure 3: Illustration of List<String> flavours transformation.
Map<String,bool> mappedFlavours Initialization

So now the question arises that how does this transformation take place? Well, we define a custom function that would take the List<String> flavours as a required parameter, process it and return a Map<String, bool> that would be stored in a variable mappedFlavours as depicted in figure 4.

Figure 4: Illustration of List transformation to Map using convertToMap function.
Figure 4: Illustration of List transformation to Map using convertToMap function.

For the sake of simplicity, we define the function signature as convertToMap(List<String>flavours).

Code to convert a List<String> into a Map<String, bool>

PREPARE FOR TAKEOFF

Photo by Ashim D’Silva on Unsplash

Now begins the interesting part, for each of these three blocks we would be writing unit test cases. To write unit tests, you don’t need any external pubspec packages, flutter already provides a test package under the dev_dependencies inside the pubspec.yaml file along with a folder named test in your project directory.

Start by creating a unit_test.dart file inside the test folder, see figure 5.

Figure 5 : Unit Test Directory Structure
Figure 5: Unit Test Directory Structure

Note: At the moment comment out the widget_test.dart file.

We would be expecting a test case to pass if the actual condition matches the given condition. How? Well, it’s quite simple to define a test case, we invoke the test method⁵ likewise.

Test Method Syntax

The expect⁶ is a synchronous method that takes two parameters.

  • actual: Its type is dynamic, so we have open hands to define any conditional expression.
  • matcher: Its type is also dynamic, it contains different matching constants & functions which are mostly derived from the Matcher⁷ class. Most of the matchers like isLessThan,isGreaterThan,isNull, isNot ,isTrue, isFalse, equals etc are self-explanatory.

Not only you can write separate test cases, but also isolate them together using the group method⁸, for example.

Group Method Syntax

The coding structure for writing a test is similar to normal coding, see the below snippet.

Unit Test Coding Structure

Visual Studio Code provides a simple user interface to run any test by navigating to the side menu, click on the test icon & choose from various options, see figure 6.

Running Tests in VS Code
Figure 6: Running Tests in VS Code

LIST TEST CASE

Following are some test cases that visualize the concept of unit tests.

CASE 1: LIST IS NULL

Illustration a Null list.
Figure 7: Illustration of a Null list.
Expecting a Null List.

As we have not initialized the List<String> flavours, we would expect it to be null as reflected in figure 7.

CASE 2: LIST IS EMPTY

Illustration of an empty list.
Figure 8: Illustration of an empty list.
Expecting an Empty List.

After initialization, we expect that the list is empty as shown in figure 8.

CASE 3: LIST IS NOT EMPTY

Illustration of a non-empty list.
Figure 9: Illustration of a non-empty list.
Expecting a non-empty List.

By assigning values to the list, we expect that the list is empty no more (see figure 9).

CASE 4: LIST HAS TOTAL FOUR FLAVOURS

Verifying the list’s length.
Figure 10: Verifying the list’s length.
Expecting length of the list is four.

To verify that the list does not exceed more than four flavours, we check the length of the list is > 0 and < 5 as illustrated in figure 10.

CASE 5: THE FIRST FLAVOUR OF LIST IS CHOCOLATE

Illustration of first flavour in the list
Figure 11: Illustration of the first flavour in the list.
Expecting Chocolate as the list’s first flavour.

The first flavour of the list should be “Chocolate” as shown in figure 11.

CASE 6: THE LAST FLAVOUR OF LIST IS PEANUT BUTTER

Illustration of last flavour in the list.
Figure 12: Illustration of the last flavour in the list.
Expecting Peanut Butter as the list’s last flavour.

For the given situation in figure 12, we require an unequal operation. As no such method exist currently, we wrap the equals method in the isNot clause, see snippet above.

CASE 7: LIST HAS TWO FLAVOURS STARTING WITH THE LETTER C

Illustration of two flavours with letter C in the list
Figure 13: Illustration of two flavours with letter C in the list
Expecting 2 flavours with the letter C

We iterate over each flavour using the list’s where method to get our desired result, then check that the length of the resultant exactly matches 2. (see figure 13)

CASE 8: LIST HAS TOTAL FIFTEEN VOWELS

Illustration of fifteen vowels in the list
Figure 14: Illustration of fifteen vowels in the list
Expecting fifteen vowels in the list.

We declare a list to hold the vowels, iterate over each flavour. Using the regular expression, we removed all consonants from the list, added these into the vowels list and expected the length to be exactly fifteen. (refer to figure 14)

CASE 9: LIST HAS TOTAL OF TWENTY CONSONANTS

Illustration of twenty consonants in the list
Figure 15: Illustration of twenty consonants in the list
Expecting twenty consonants in the list.

As shown in figure 15, we only need to count the consonants by removing out the vowels using the same previous technique but with a different regular expression.

CASE 10: LISTS ARE NOT SAME

Illustration of unequal lists.
Figure 16: Illustration of unequal lists.
Expecting Unequal Lists.

In figure 16, it seems obvious that the lists are not the same. To create the test, we simply compare the flavours list with an empty list and expect it to be false.

MAP TEST CASES

Following are test cases for the mappedFlavours variable:

CASE 1: MAP IS NOT NULL

Illustration of non null map .
Figure 17: Illustration of the non-null map.
Expecting a non-null Map

Since the mappedFlavours was declared with an empty entry, therefore it cannot be null. (see figure 17)

CASE 2: MAP IS EMPTY

Illustration of an empty map.
Figure 18: Illustration of an empty map.
Expecting an Empty Map.

To check that the map is empty (as in figure 18), simply call the isEmpty matcher.

CASE 3: MAP KEY IS A STRING

Illustration of Map Key.
Figure 19: Illustration of Map Key.
Expecting Map Key is String.

To confirm the data type of map is a string, we compare the key’s runtime type & expect that it doesn’t match other primitive data types. (see figure 19)

CASE 4: MAP VALUE IS A BOOL

Figure 20: Illustration of Map Value.
Expecting Map Value is bool.

In the case of figure 20, applying the same procedure we can check the map’s value as well.

FUNCTION TEST CASES

CASE 1: convertToMap REQUIRES A LIST<STRING> PARAMETER

Illustration of Required Function Parameter.
Figure 21: Illustration of Required Function Parameter.
Expecting a required function parameter.

To ensure that the function requires a parameter (as in figure 21), we compare it with a custom function signature having no parameter & expect it to be false.

CASE 2: convertToMap RETURN TYPE IS A MAP<STRING, BOOL>

Illustration of valid function return type.
Figure 22: Illustration of the valid function return type.
Expecting a valid return type.

To assure the function’s return type is Map<String, bool> (as in figure 22), we define a function with the same parameter signature body. After comparison, we expect it to be true.

CASE 3: mappedFlavour SHOULD NOT BE EMPTY

Illustration of map after function call.
Figure 23: Illustration of the map after the function call.
Expecting a non-empty map after the function call.

After invoking the convertToMap function, we expect that the mappedFlavours variable will not be empty any more (refer to figure 23).

POST CONVERSION TEST CASES

Below are the test cases that would cover up the transformations made to the mappedFlavours after the convertToMap function was called.

CASE 1: mappedFlavour HAS NO KEY CHERRY

Illustration of invalid map entry.
Figure 24: Illustration of an invalid map key.
Expecting a valid map key only.

After transformation, we expect that no invalid key exists in the map(see figure 24).

CASE 2: mappedFlavour HAS NO VALUE 0

Illustration of invalid map value.
Figure 25: Illustration of invalid map value.
Expecting a valid map value only.

After transformation, we expect that no invalid value exists in the map(see figure 25).

CASE 3: mappedFlavour HAS NO BLANK ENTRIES

Figure 26: Illustration of invalid map entries.
Expecting valid map entries.

In the case of figure 26, we validate all the entries of the map are not blank.

FINAL RESULTS

Full Source Code
A gif that shows the final outcome of the unit test cases.
Final Outcome

WAIT THERE’S MORE?

Photo by Clay LeConey on Unsplash

Don’t think it’s over yet! We still have a long road journey towards the end to end testing part. If you learned something today, then go ahead, enhance the code, add more test cases. Pull requests are most welcome in the repository. Hope to see you soon in the widget testing phase…..

REFERENCES

[1]: 7 Benefits Of Software Testing and Quality Assurance
http://www.avantica.com/blog/qa-benefits

[2]: The Practical Test Pyramid
https://martinfowler.com/articles/practical-test-pyramid.html

[3]: Multi-Select Dialogue in Flutter: A Versatile Approach
https://levelup.gitconnected.com/multi-select-dialogue-in-flutter-a-versatile-approach

[4]: flutter_test library — Dart API
https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html

[5]: test function — flutter_test library — Dart API
https://api.flutter.dev/flutter/flutter_test/test.html

[6]: expect function — flutter_test library — Dart API
https://api.flutter.dev/flutter/flutter_test/expect.html

[7]: group function — flutter_test library — Dart API
https://api.flutter.dev/flutter/flutter_test/group.html

[8]: matcher library — Dart API
https://api.flutter.dev/flutter/package-matcher_matcher/package-matcher_matcher-library.html

[9]: Multiple Selection Dialogue App Github Repo
https://github.com/Zujaj/multiple_selection_dialogue_app.git

--

--

A passionate flutter developer who loves to learn, explore and share knowledge. Serving at https://telicsolutions.com/