Fluttering Dart

Fluttering Dart: Unit Testing

How to write modular, efficient and bug-free code

Photo by Louis Reed on Unsplash

Unit tests come to rescue when it comes to write performant and (almost) bug-free code.

It is not the only option for testing our code, but it is the option that allows us to test small pieces of software in isolation.

Complete code coverage doesn’t remove entirely the possibility of bugs popping-up in the code but ensures a good development cycle and more stable releases.

There are a lot of benefits provided by using unit testing:

  • early detection of bugs, and ease of debugging
  • confidence when adding new features
  • better software design, when doing test-driven development (TDD)
  • overview of our entire software system

Tests are written to confirm that code behaves as expected on normal input (the happy flow or sunny-side testing) or on non-normal input and cases (unhappy flow or rainny-side testing).

These benefits sum up and result in high quality and cost-effective code in the long run.

Dart provides some tools to handle tests and we’re going to explore those.

The Dart test package

This package is not part of the Dart SDK and has to be added as a dependency.

It can be added as one of our dev_dependencies.

dev_dependencies:
test: ^1.0.0

You can find out more about Dart packages and how to add dependencies here.

With the test libraries added we can now write unit tests.

Dart unit tests

Let’s say we have a Randomizer class that returns a number within a range:

class Randomizer { 
num nextInRange(num a, num b) {
// TBI
}
}

This is how the unit test for the above method will look:

import 'package:test/test.dart';
import 'package:fluttering_dart/randomizer.dart';
void main() {
Randomizer r;
setUp(() {
r = Randomizer();
});
test('returns a random number in range', () {
expect(r.nextInRange(1, 3), 2);
});
}

We used 3 methods that the test package library provides:

  • setUp() — will execute the callback function we pass as a parameter before each of the tests in the suite
  • test() — the test that receives as parameters a description and a callback function that implements the test
  • expect() — used to make assertions on the test; above we (wrongfully) assert that a random number between 1 and 3 will be equal to 2

In order to run the test we execute this command template, where <test_file> is the path to our file:

pub run test <test_file>

As said above we wrongfully asserted that 2 will always be returned when calling for a random number within the 1–3 range.

Depending on the implementation, the returned value can be an int with possible values [1, 2, 3] or a double ranging from 1.0 to 3.0.

Right now the test will fail randomly, passing only when the random number that is returned by the nextInRange method is 2.

pub run test test/randomizer_test.dart 
00:01 +0 -1: returns a random number in range [E]
Expected: <2>
Actual: <1>

package:test_api expect
test/randomizer_test.dart 12:5 main.<fn>

00:01 +0 -1: Some tests failed.

To fix this we will change or test to:

test('returns random number in range', () {
expect(
r.nextInRange(1, 3),
allOf(
greaterThanOrEqualTo(1),
lessThanOrEqualTo(3)
)
);
});

Now our test will pass every time.

pub run test test/randomizer_test.dart 
00:01 +1: All tests passed!

We can also create groups of tests to cover all of our test cases. This is how it’s done:

import 'package:test/test.dart';
import 'package:fluttering_dart/randomizer.dart';
void main() {
Randomizer r;
setUp(() {
r = Randomizer();
});
group('more than one test case', () {
test('returns a random double number in range', () {
expect(
r.nextInRange(1.9, 8.6),
allOf(
greaterThanOrEqualTo(1.9),
lessThanOrEqualTo(8.6)
)
);
});
test('returns a random int number in range', () {
expect(
r.nextInRange(1, 3),
allOf(
greaterThanOrEqualTo(1),
lessThanOrEqualTo(3)
)
);
});
});
}

This is just a brief run-through the unit testing capabilities that Dart puts at hand.

For a complete overview and deeper insight you can go at the official test package page on pub.dev

In the previous parts of the series, we went through the Dart built-in data types, functions, operators, control flow statements, object-orientated programming (classes, objects and more), asynchronous programming, and libraries and packages.

In this last part focused on Dart we’ve played a bit with unit testing in Dart.

If you missed out on any of the above you can find them here:

I hope that the Fluttering Dart series helped you, so far, discover helpful and interesting Dart fundamentals needed for building better Flutter multi-platform apps.

Tha(nk|t’)s all!

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