Testing leads to understanding….

How to test providers in Flutter, hmmm….

Installing provider,

Put this dependency in your pubspec.yaml.

provider: ^3.1.0+1 // as of now

Begin Testing…

Pre-requisite :

Install build_runner, json_serializable and json_annotation….

Note : Please place the packages as advised in the respective links..

Also, you need to be in flutter channel : stable and version :Flutter 1.9.1+hotfix.6

1. Create a Model class…

We have created a simple model class called Location….

part 'location.g.dart';
@JsonSerializable()
class Location {
//
Location({
this.city,
this.country,
this.countryName,
this.currency,
this.inEu,
this.ip,
this.languages,
});
@JsonKey(name: 'city')
String city;
@JsonKey(name: 'country')
String country;
@JsonKey(name: 'country_name')
String countryName;
@JsonKey(name: 'currency')
String currency;
@JsonKey(name: 'in_eu')
bool inEu;
@JsonKey(name: 'ip')
String ip;
@JsonKey(name: 'languages')
String languages;
factory Location.fromJson(Map<String, dynamic> json) =>
_$LocationFromJson(json);
Map<String, dynamic> toJson() => _$LocationToJson(this);
}

Next, we will use the flutter generator to create generated file….by running 

flutter pub run build_runner build --delete-conflicting-outputs

This generates location.g.dart file….


2. Write Unit Test….

All tests for flutter should be placed in the test folder (generated by default for every project..)

According to Flutter Documentation :

A unit test tests a single function, method, or class. The goal of a unit test is to verify the correctness of a unit of logic under a variety of conditions.

For more info, check out this link..

Testing provider in Flutter
Testing provider in Flutter

We will write test inside provider_test.dart

void main() async {
//
Location _locationModel;
StreamController<Location> _controller;
setUp(() {
_controller = StreamController<Location>();
_locationModel = Location(city: 'London');
});
group('[Location Model]', () {
//
test('[Model] Check individual values', () async {
_locationModel = Location(
city: 'London',
countryName: 'England',
country: 'England',
currency: 'GBP',
ip: '',
inEu: true,
languages: 'EN',
);
// BEGIN TESTS....
expect(_locationModel.city, 'London');
expect(_locationModel.countryName.runtimeType, equals(String));
expect(_locationModel.country, isNotNull);
expect(_locationModel.ip, isEmpty);
expect(_locationModel.inEu, isTrue);
expect(_locationModel.languages, contains('EN'));
expect(_locationModel.currency, startsWith('G'));
expect(_locationModel.country, matches('England'));
});
}
  • Initialize a group of test (named Location Model)…
  • Include a test (named [Model] Check individual values)…

Use of setUp…

Registers a function to be run before tests. This function will be called before each test is run. We utilize this function to initialize a location model with default value and a stream controller of location model….

Use of expect…

It checks if actual matches matcher , for instance say,

expect(_locationModel.city, 'London');

Here, _locationModel.city is actual and ‘London’ is matcher….

We can check the following :

  • Value: 
expect(_locationModel.city, 'London');
  • RuntimeType: 
expect(_locationModel.countryName.runtimeType, equals(String));
  • Null / IsNotNull:
expect(_locationModel.country, isNotNull);
  • IsEmpty
expect(_locationModel.ip, isEmpty);
  • isTrue / isFalse
expect(_locationModel.inEu, isTrue);
  • Contains
expect(_locationModel.languages, contains('EN'));
  • StartsWith
expect(_locationModel.currency, startsWith('G'));
  • Matches
expect(_locationModel.country, matches('England'));

3. Write Widget Test…

Inside the group created above (in step 2), add one more test….

testWidgets('[Provider] Update when the value changes', (tester) async {
final _providerKey = GlobalKey();
final _childKey = GlobalKey();
BuildContext context;
await tester.pumpWidget(StreamProvider(
key: _providerKey,
builder: (c) {
context = c;
return _controller.stream;
},
child: Container(key: _childKey),
));
// Check the context test...
expect(context, equals(_providerKey.currentContext));
// Check the model test (if null)...
expect(Provider.of<Location>(_childKey.currentContext), null);
_controller.add(Location(city: 'London'));
// Delay the pump...
await Future.microtask(tester.pump);
// Check if the model passed (with some value) is the same as received...
expect(
Provider.of<Location>(_childKey.currentContext).toJson(),
_locationModel.toJson(),
);
await _controller.close();
});

Use of testWidgets:

Use this function for testing custom StatelessWidgets and StatefulWidgets. In our case, we create a widget inside the test itself and using the Key property…

Use of pumpWidgets :

Renders the UI from the given widget. The widget created inside the test is used inside pumpWidget…

// Check the context test...
expect(context, equals(_providerKey.currentContext));

here, we check the context of the widget against the context of provider widget…

// Check the model test (if null)...
expect(Provider.of<Location>(_childKey.currentContext), null);

first time, provider loads with null value, we have checked this case here

// Check if the model passed (with some value) is the same as received...
expect(
Provider.of<Location>(_childKey.currentContext).toJson(),
_locationModel.toJson(),
);

We add some value to the provider model, and then delay the pumping of widget, so that now it has the latest value inside provider…In our case, the model data loaded initially is compared against the one which we added before delay…

Run the test…

flutter test test/providers_test/provider_test.dart
Testing provider in Flutter
Testing provider in Flutter

Source code :

https://github.com/AseemWangoo/flutter_programs/blob/master/providers_test.zip

Leave a Reply

Your email address will not be published. Required fields are marked *