无法找到 TextField 文本数据

kqc*_*cef 8 flutter flutter-test

我正在尝试为TextField我使用的地方编写一个测试IconButton作为 TextField 的,suffixIcon以便在按下时,它将切换 TextField 的obscureText属性。小部件本身按预期工作,但我很难接近测试。

代码示例:

密码字段.dart

import 'package:flutter/material.dart';
import 'package:app/util/app_theme.dart';
import 'package:app/widgets/inputs/text_validator.dart';

class PasswordField extends StatefulWidget {
  final bool showValidator;
  final ValueChanged<String> onChanged;
  final String errorText;

  PasswordField({
    this.errorText,
    @required this.onChanged,
    this.showValidator = true,
  });

  @override
  _PasswordFieldState createState() => _PasswordFieldState();
}

class _PasswordFieldState extends State<PasswordField> {
  String _password;
  bool _shouldHidePassword = true;

  void _onChangedPassword(String password) {
    setState(() {
      _password = password;
    });
    widget.onChanged(password);
  }

  void _togglePasswordVisibility() {
    setState(() {
      _shouldHidePassword = !_shouldHidePassword;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          onChanged: _onChangedPassword,
          decoration: _buildInputDecoration(),
          obscureText: _shouldHidePassword,
        ),
      ],
    );
  }

  InputDecoration _buildInputDecoration() {
    return InputDecoration(
      suffixIcon: IconButton(
        icon: Icon(
          _shouldHidePassword ? CustomIcons.eye_show : CustomIcons.eye_hide),
        onPressed: _togglePasswordVisibility,
      ),
      labelText: 'Password',
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

password_field_test.dart

testWidgets('should show an eye that obscures/unobscures the typed values', (WidgetTester tester) async {
      // buildWidget just wraps target in a MediaQuery/Directionality/Material
      Widget field = buildWidget(PasswordField(onChanged: (_) {}));
      String testPW = 'passwordtotest';

      await tester.pumpWidget(field);
      expect(find.byType(TextField), findsOneWidget);


      await tester.enterText(find.byType(TextField), testPW);
      await tester.pump();

      // These are what I've tried so far
      expect(find.text('•' * testPW.length), findsOneWidget)
      expect(find.byElementType(TextSpan))


      await tester.tap(find.byType(IconButton));
      await tester.pump();

      expect(find.text(testPW), findsOneWidget);
    });
  });
Run Code Online (Sandbox Code Playgroud)

到目前为止,我已经尝试遵循 Flutter 小部件测试文档,但我无法通过 find.text('demopassword');

我也尝试将查找器用于底层 EditableText 和 TextSpan,但未成功,但即使我能够找到它们,我也不知道如何定位文本值。我怎样才能做到这一点?

小智 0

在上面的小部件中,虽然我们明显显示“*”或纯文本,但文本的存储始终是纯文本。因此,当试图通过测试来检查它时,它永远无法找到隐藏的密码。理想情况下,在测试模糊性时,我们应该只检查它们的标志状态(obscureText)。

尝试对您的测试进行以下修改 -

  • 第 1 步:检查密码是否以模糊文本呈现
  • 第 2 步:点击图标按钮
  • 步骤3:检查密码是否以明文形式呈现

testWidgets('should show an eye that obscures/unobscures the typed values',
      (WidgetTester tester) async {
    // buildWidget just wraps target in a MediaQuery/Directionality/Material
    Widget field = buildWidget(PasswordField(onChanged: (_) {}));
    String testPW = 'passwordtotest';

    await tester.pumpWidget(field);
    expect(find.byType(TextField), findsOneWidget);

    await tester.enterText(find.byType(TextField), testPW);
    await tester.pump();

    expect(
        (tester.firstWidget(find.byType(TextField)) as TextField).obscureText,
        true);

    await tester.tap(find.byType(IconButton));
    await tester.pump();

    expect(
        (tester.firstWidget(find.byType(TextField)) as TextField).obscureText,
        false);
  });
Run Code Online (Sandbox Code Playgroud)

此外,作为工程建议,您实际上可以尝试使用 ValueListenableBuilder,而不是在代码中使用 setState()。它很简单,可以避免不必要的状态管理麻烦并防止性能影响。更多参考: https: //api.flutter.dev/flutter/widgets/ValueListenableBuilder-class.html

使用 ValueListenableBuilder,您的构建方法代码看起来有点像这样 -

Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ValueListenableBuilder<bool>(
          valueListenable: shouldHidePassword,
          builder: (context, bool value, child) {
            return TextField(
              onChanged: _onChangedPassword,
              decoration: InputDecoration(
                suffixIcon: IconButton(
                  icon: Icon(shouldHidePassword.value
                      ? Icons.remove_red_eye_outlined
                      : Icons.remove_red_eye),
                  onPressed: () =>
                      shouldHidePassword.value = !shouldHidePassword.value,
                ),
                labelText: 'Password',
              ),
              obscureText: value,
            );
          },
        ),
      ],
    );
  }
Run Code Online (Sandbox Code Playgroud)

更干净、更容易维护;而您以后可以避免一堆性能问题!