Skip to content

Commit

Permalink
Make tests null safe (#904)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuisDuarte1 authored Sep 10, 2023
2 parents 43e6780 + 006e281 commit 86a2078
Show file tree
Hide file tree
Showing 28 changed files with 1,967 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
env:
PROPERTIES_PATH: "android/key.properties"
JAVA_VERSION: "11.x"
FLUTTER_VERSION: "3.7.2"
FLUTTER_VERSION: "3.10.6"
defaults:
run:
working-directory: ./uni
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/format_lint_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches: [master, develop]

env:
FLUTTER_VERSION: 3.7.2
FLUTTER_VERSION: 3.10.6
JAVA_VERSION: 11.x

jobs:
Expand All @@ -20,7 +20,7 @@ jobs:
with:
flutter-version: ${{ env.FLUTTER_VERSION }}

- run: dart format $(find . -type f -name "*.dart" -a -not -name "*.g.dart") --set-exit-if-changed
- run: dart format $(find . -type f -name "*.dart" -a -not -name "*.g.dart" -a -not -name "*.mocks.dart") --set-exit-if-changed

lint:
name: "Lint"
Expand Down
2 changes: 1 addition & 1 deletion pre-commit-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mkdir -p .git/hooks #it seems that are some cases where git will not create a ho
tee .git/hooks/pre-commit << EOF
#!/bin/sh
FILES="\$(git diff --name-only --cached | grep .*\.dart | grep -v .*\.g\.dart)"
FILES="\$(git diff --name-only --cached | grep .*\.dart | grep -v .*\.g\.dart | grep -v .*\.mocks\.dart)"
[ -z "\$FILES" ] && exit 0
Expand Down
28 changes: 28 additions & 0 deletions uni/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ In order to remove it, is it as simple as running the following command, from th
rm .git/hooks/pre-commit
```

### Generated files

Flutter doesn't support runtime reflection. In order to circumvent these limitations, we use **automatic code generation** or **static metaprogramming** for things like **mocks** and other possible usecases. By convention, you should **always commit** the generated `.dart` files into the repository.

Dart leverages annotations to signal the `build_runner` that it should generate some code. They look something like this:
```dart
import 'package:mockito/annotations.dart'
class Cat{
}
@GenerateNiceMocks([MockSpec<Cat>()])
void main(){
}
```
In this case, `build_runner` will detect that `GenerateNiceMocks` is a generator function from `mockito` and will generate code to a different file.

In order to run the `build_runner` once:
```sh
dart run build_runner build
```

But you can also watch for changes in `.dart` files and automatically run the `build_runner` on those file changes (useful if you find yourself in need to generate code very frequently):
```sh
dart run build_runner watch
```

## Project structure

### Overview
Expand Down
1 change: 1 addition & 0 deletions uni/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ analyzer:
# Exclude auto-generated files from dart analysis
exclude:
- "**.g.dart"
- "**.mocks.dart"

# Custom linter rules. A list of all rules can be found at
# https://dart-lang.github.io/linter/lints/options/options.html
Expand Down
7 changes: 7 additions & 0 deletions uni/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
targets:
$default:
builders:
mockito|mockBuilder:
options:
build_extensions:
'^test/{{}}.dart': 'test/mocks/{{}}.mocks.dart'
3 changes: 1 addition & 2 deletions uni/lib/controller/local_storage/app_bus_stop_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ class AppBusStopDatabase extends AppDatabase {
}

final stops = <String, BusStopData>{};
groupBy(buses, (stop) => (stop! as Map<String, dynamic>)['stopCode'])
.forEach(
groupBy(buses, (stop) => stop['stopCode']).forEach(
(stopCode, busCodeList) => stops[stopCode as String] = BusStopData(
configuredBuses: Set<String>.from(
busCodeList.map((busEntry) => busEntry['busCode']),
Expand Down
2 changes: 1 addition & 1 deletion uni/lib/model/providers/lazy/bus_stop_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BusStopProvider extends StateProviderNotifier {

Future<void> addUserBusStop(String stopCode, BusStopData stopData) async {
if (_configuredBusStops.containsKey(stopCode)) {
(_configuredBusStops[stopCode]!.configuredBuses).clear();
_configuredBusStops[stopCode]!.configuredBuses.clear();
_configuredBusStops[stopCode]!
.configuredBuses
.addAll(stopData.configuredBuses);
Expand Down
2 changes: 1 addition & 1 deletion uni/lib/model/providers/lazy/lecture_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class LectureProvider extends StateProviderNotifier {
Session session,
Profile profile,
) =>
(fetcher?.getLectures(session, profile)) ?? getLectures(session, profile);
fetcher?.getLectures(session, profile) ?? getLectures(session, profile);

Future<List<Lecture>> getLectures(Session session, Profile profile) {
return ScheduleFetcherApi()
Expand Down
8 changes: 4 additions & 4 deletions uni/lib/view/bug_report/widgets/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class BugReportFormState extends State<BugReportForm> {

bugDescriptions.forEach(
(int key, Tuple2<String, String> tup) =>
{bugList.add(DropdownMenuItem(value: key, child: Text(tup.item1)))},
bugList.add(DropdownMenuItem(value: key, child: Text(tup.item1))),
);
}

Expand Down Expand Up @@ -117,9 +117,9 @@ class BugReportFormState extends State<BugReportForm> {
Widget bugReportTitle(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10),
child: Row(
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const <Widget>[
children: <Widget>[
Icon(Icons.bug_report, size: 40),
PageTitle(name: 'Bugs e Sugestões', center: false),
Icon(Icons.bug_report, size: 40),
Expand Down Expand Up @@ -172,7 +172,7 @@ class BugReportFormState extends State<BugReportForm> {
value: _selectedBug,
onChanged: (value) {
setState(() {
_selectedBug = value! as int;
_selectedBug = value!;
});
},
isExpanded: true,
Expand Down
4 changes: 2 additions & 2 deletions uni/lib/view/exams/widgets/exam_page_title.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class ExamPageTitle extends StatelessWidget {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
alignment: Alignment.center,
child: Row(
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
children: [
PageTitle(name: 'Exames', center: false, pad: false),
Material(child: ExamFilterMenu()),
],
Expand Down
2 changes: 1 addition & 1 deletion uni/lib/view/login/login.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class LoginPageViewState extends State<LoginPageView> {

/// Tracks if the user wants to keep signed in (has a
/// checkmark on the button).
void _setKeepSignedIn(bool? value) {
void _setKeepSignedIn({bool? value}) {
if (value == null) return;
setState(() {
_keepSignedIn = value;
Expand Down
4 changes: 2 additions & 2 deletions uni/lib/view/login/widgets/inputs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ Widget createPasswordInput(

/// Creates the widget for the user to keep signed in (save his data).
Widget createSaveDataCheckBox(
void Function(bool?)? setKeepSignedIn, {
void Function({bool? value})? setKeepSignedIn, {
required bool keepSignedIn,
}) {
return CheckboxListTile(
value: keepSignedIn,
onChanged: setKeepSignedIn,
onChanged: (value) => setKeepSignedIn?.call(value: value),
title: const Text(
'Manter sessão iniciada',
textAlign: TextAlign.center,
Expand Down
4 changes: 2 additions & 2 deletions uni/lib/view/restaurant/restaurant_page_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ class _RestaurantPageViewState extends GeneralPageViewState<RestaurantPageView>
return Container(
margin: const EdgeInsets.only(top: 10, bottom: 5),
key: Key('restaurant-page-day-column-$day'),
child: Column(
child: const Column(
mainAxisSize: MainAxisSize.min,
children: const [
children: [
Center(
child: Text('Não há informação disponível sobre refeições'),
),
Expand Down
4 changes: 2 additions & 2 deletions uni/lib/view/useful_info/widgets/other_links_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class OtherLinksCard extends GenericExpansionCard {

@override
Widget buildCardContent(BuildContext context) {
return Column(
children: const [
return const Column(
children: [
// LinkButton(title: 'Impressão', link: 'https://print.up.pt'),
// TODO(Process-ing): Get fixed link
LinkButton(
Expand Down
4 changes: 2 additions & 2 deletions uni/lib/view/useful_info/widgets/sigarra_links_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class SigarraLinksCard extends GenericExpansionCard {

@override
Widget buildCardContent(BuildContext context) {
return Column(
children: const [
return const Column(
children: [
LinkButton(
title: 'Notícias',
link: 'https://sigarra.up.pt/feup/pt/noticias_geral.lista_noticias',
Expand Down
12 changes: 9 additions & 3 deletions uni/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ publish_to: 'none' # We do not publish to pub.dev
version: 1.5.58+176

environment:
sdk: ">=2.17.1 <3.0.0"
flutter: 3.7.2
sdk: '>=3.0.0 <4.0.0'
flutter: 3.10.6

# Dependencies specify other packages that the application needs in order to work.
# Major versions and critical security upgrades are managed by dependabot, and
Expand Down Expand Up @@ -59,10 +59,16 @@ dependencies:


dev_dependencies:
build_runner: ^2.4.6
flutter_launcher_icons: ^0.13.1
flutter_test:
sdk: flutter
mockito: ^5.2.0
# FIXME(luisd): Update mockito to a release version when 5.4.3 or above
# is launched
mockito:
git:
url: https://github.com/dart-lang/mockito.git
ref: "e54a006"
test: any
very_good_analysis: ^4.0.0+1

Expand Down
5 changes: 0 additions & 5 deletions uni/test/analysis_options.yaml

This file was deleted.

9 changes: 3 additions & 6 deletions uni/test/integration/src/exams_page_test.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// @dart=2.10

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
Expand All @@ -18,12 +17,10 @@ import 'package:uni/model/entities/session.dart';
import 'package:uni/model/providers/lazy/exam_provider.dart';
import 'package:uni/view/exams/exams.dart';

import '../../mocks/integration/src/exams_page_test.mocks.dart';
import '../../test_widget.dart';

class MockClient extends Mock implements http.Client {}

class MockResponse extends Mock implements http.Response {}

@GenerateNiceMocks([MockSpec<http.Client>(), MockSpec<http.Response>()])
void main() {
group('ExamsPage Integration Tests', () {
final mockClient = MockClient();
Expand Down
15 changes: 7 additions & 8 deletions uni/test/integration/src/schedule_page_test.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// @dart=2.10

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
Expand All @@ -17,22 +16,22 @@ import 'package:uni/model/providers/lazy/lecture_provider.dart';
import 'package:uni/model/providers/startup/session_provider.dart';
import 'package:uni/view/schedule/schedule.dart';

import '../../mocks/integration/src/schedule_page_test.mocks.dart';
import '../../test_widget.dart';
import '../../unit/view/Widgets/schedule_slot_test.dart';

class MockClient extends Mock implements http.Client {}

class MockResponse extends Mock implements http.Response {}

class MockSessionProvider extends Mock implements SessionProvider {}

class UriMatcher extends CustomMatcher {
UriMatcher(Matcher matcher) : super('Uri that has', 'string', matcher);

@override
Object featureValueOf(dynamic actual) => (actual as Uri).toString();
}

@GenerateNiceMocks([
MockSpec<http.Client>(),
MockSpec<http.Response>(),
MockSpec<SessionProvider>()
])
void main() {
group('SchedulePage Integration Tests', () {
final mockClient = MockClient();
Expand Down
Loading

0 comments on commit 86a2078

Please sign in to comment.