нажмите элемент из выпадающего меню. Элемент не вызывает кнопку раскрывающегося списка.Измененный тест виджета трепещет

#flutter #flutter-test

Вопрос:

В моем коде, когда страна выбирается из раскрывающейся кнопки, под ней отображается форма. При тестировании этого виджета я не знаю, как заставить функцию onchanged запускаться на кнопке раскрывающегося списка, когда я нажимаю элемент из выпадающего меню. У меня есть инструкция print в функции onchanged, которую я не вижу на консоли отладки при запуске теста. Тест также завершается неудачно при запуске последнего ожидаемого() раскомментированного кода.

КОНСОЛЬ ОТЛАДКИ

 ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
  Expected: 'Afghanistan'
  Actual: 'Select Country'
   Which: is different.
          Expected: Afghanista ...
            Actual: Select Cou ...
                    ^
           Differ at offset 0

When the exception was thrown, this was the stack:
#4      main.<anonymous closure> (file:///C:/Users/calvo/Documents/flutter/projects/freegapp/test/PersonalInfo_widget_test.dart:48:5)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...

This was caught by the test expectation on the following line:
  file:///C:/Users/calvo/Documents/flutter/projects/freegapp/test/PersonalInfo_widget_test.dart line 48
The test description was:
  make profile
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: make profile

✖ make profile
Exited (1)

 

PersonalInfo.дарт

 import 'package:flutter/material.dart';
import 'package:freegapp/Selling.dart';
import 'package:freegapp/src/MyUserInfo.dart';
import 'package:image_picker/image_picker.dart';
import 'package:freegapp/src/mocks/ImagePickerMock.dart';
import 'dart:io';
import 'package:freegapp/src/ApplicationStateFirebase.dart';
import 'package:freegapp/src/mocks/ApplicationStateFirebaseMock.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter/services.dart';
import 'package:flutter/src/services/asset_bundle.dart' show rootBundle;

class PersonalInfo extends StatefulWidget {
  PersonalInfo({
    required this.logout,
    required this.myUserInfo,
    Key? key,
  }) : super(key: key); // Initializes key for subclasses.
  final void Function() logout;
  final MyUserInfo myUserInfo;
  @override
  _PersonalInfoState createState() => _PersonalInfoState();
}

class _PersonalInfoState extends State<PersonalInfo> {
  final homeAddressController = TextEditingController();
  final phoneNumberController = TextEditingController();
  final latitudeController = TextEditingController();
  final longitudeController = TextEditingController();
  List<XFile>? _imageFileList;
  var currentAddress;
  var currentPosition;

  final ImagePicker _picker = ImagePicker();
  final ImagePickerMock _mockPicker = ImagePickerMock();
  final _formKey = GlobalKey<FormState>(debugLabel: '_PersonalInfoStateForm');
  var _formIsVisible = true;
  String? countriesJsonString;
  final defaultDropDownValue = 'Select Country';
  var dropdownValue;

  @override
  void initState() {
    super.initState();
    dropdownValue = widget.myUserInfo.country ?? defaultDropDownValue;
    homeAddressController.text = widget.myUserInfo.homeAddress == null
        ? ''
        : widget.myUserInfo.homeAddress.toString();
    phoneNumberController.text = widget.myUserInfo.phoneNumber == null
        ? ''
        : widget.myUserInfo.phoneNumber.toString();
    latitudeController.text = widget.myUserInfo.latitude == null
        ? ''
        : widget.myUserInfo.latitude.toString();
    longitudeController.text = widget.myUserInfo.longitude == null
        ? ''
        : widget.myUserInfo.longitude.toString();
  }

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the
    // widget tree.
    homeAddressController.dispose();
    phoneNumberController.dispose();
    latitudeController.dispose();
    longitudeController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        Container(
            height: 160,
            width: MediaQuery.of(context).size.width,
            child: imageProfile(widget.myUserInfo.profilePic)),
        SizedBox(
          height: 20,
        ),
        FutureBuilder(
            future: loadCountriesFromAsset(),
            builder: (context, snapshot) {
              if (snapshot.connectionState != ConnectionState.done) {
                // futurer hasn't finished yet, return a place holder
                return Text('Loading');
              }
              return DropdownButton<String>(
                key: Key('DropdownButtonPersonalInfo'),
                value: dropdownValue,
                icon: const Icon(Icons.arrow_downward),
                iconSize: 24,
                elevation: 16,
                style: const TextStyle(color: Colors.deepPurple),
                underline: Container(
                  height: 2,
                  color: Colors.deepPurpleAccent,
                ),
                onChanged: (String? newValue) {
                  setState(() {
                    print('we inside');
                    dropdownValue = newValue!;
                    if (dropdownValue == defaultDropDownValue) {
                      _formIsVisible = false;
                    } else {
                      _formIsVisible = true;
                    }
                  });
                },
                items: List<String>.from(
                        jsonDecode(countriesJsonString.toString())['names'])
                    .map<DropdownMenuItem<String>>((String value) {
                  return DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  );
                }).toList(),
              );
            }),
        Expanded(
            child: Visibility(
                visible: _formIsVisible, child: form(widget.myUserInfo))),
      ],
    ));
  }

  Future<dynamic> loadCountriesFromAsset() async {
    final contents = await rootBundle.loadString('assets/countries.json');
    countriesJsonString = contents;
    if (dropdownValue == defaultDropDownValue) {
      _formIsVisible = false;
    } else {
      _formIsVisible = true;
    }
  }
...

 

PersonalInfo_widget_test.dart

 import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:freegapp/PersonalInfo.dart';
import 'package:freegapp/src/mocks/ApplicationStateFirebaseMock.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';
// import 'dart:io';

void main() {
  testWidgets('make profile', (WidgetTester tester) async {
    await tester.pumpWidget(ChangeNotifierProvider(
        create: (context) => ApplicationStateFirebaseMock(),
        builder: (context, _) => MaterialApp(
            home: Consumer<ApplicationStateFirebaseMock>(
                builder: (context, appState, _) => PersonalInfo(
                    key: Key('PersonalInfo'),
                    logout: () {
                      appState.signOut;
                    },
                    myUserInfo: appState.myUserInfo)))));
    await tester.pumpAndSettle();
    expect(find.byKey(Key('PersonalInfo')), findsOneWidget);
    expect(
        find.byKey(Key('profilePicCircleAvatarPersonalInfo')), findsOneWidget);
    await tester.tap(find.byIcon(Icons.camera_alt_outlined));
    await tester.pumpAndSettle();
    await tester.tap(find.byIcon(Icons.image));
    await tester.pumpAndSettle();
    expect(
        find.byKey(Key('profilePicCircleAvatarPersonalInfo')), findsOneWidget);
    // expect(find.text('Select Country'), findsOneWidget);
    expect(
        (tester.widget(find.byKey(Key('DropdownButtonPersonalInfo')))
                as DropdownButton)
            .value,
        equals('Select Country'));
    expect(find.byKey(Key('ListViewFormPersonalInfo')), findsNothing);
    await tester.tap(find.byIcon(Icons.arrow_downward), warnIfMissed: false);
    // await tester.tap(find.byIcon(Icons.arrow_downward), warnIfMissed: false);
    await tester.pump();
    await tester.pump(Duration(seconds: 1));
    // after opening the menu we have two widgets with text 'Afghanistan'
    // one in index stack of the dropdown button and one in the menu .
    // apparently the last one is from the menu.
    await tester.tap(find.text('Afghanistan').last, warnIfMissed: false);
    await tester.pump();
    await tester.pump(Duration(seconds: 1));
    expect(
        (tester.widget(find.byKey(Key('DropdownButtonPersonalInfo')))
                as DropdownButton)
            .value,
        equals('Afghanistan'));
    // expect(find.byKey(Key('ListViewFormPersonalInfo')), findsOneWidget);
  });
}

 

countries.json sample

 {
    "names": ["Select Country",
    "Afghanistan", 
    "Åland Islands", 
    "Albania", 
    "Algeria", 
    "American Samoa", 
    "Andorra", 
    "Angola", 
    "Anguilla", 
    "Antarctica", 
    "Antigua and Barbuda", 
    "Argentina", 
    "Armenia", 
    "Aruba", 
    "Australia", 
    "Austria", 
    "Azerbaijan", 
    "Bahamas", 
    "Bahrain" 
    ]
}