【问题标题】:How to unit Test a Flutter TextFormField maxlines如何对 Flutter TextFormField 进行单元测试
【发布时间】:2019-09-18 13:01:16
【问题描述】:

是否可以编写一个单元测试来验证 TextFormField 的 maxLines 属性是否设置正确。我找不到访问该属性的方法:

我创建了一个 TextFormField

final field = TextFormField(
    initialValue: "hello",
    key: Key('textformfield'),
    maxLines: 2,
  );

然后在测试中我可以使用 tester.widget 访问表单字段

 final formfield =
    await tester.widget<TextFormField>(find.byKey(Key('textformfield')));

但由于 maxLines 属性被传递给返回文本字段的 Builder,我如何才能访问文本字段。

或者是否有完全其他的方法来验证这一点?

【问题讨论】:

    标签: unit-testing testing flutter widget


    【解决方案1】:

    我不知道这是否是一个好的解决方案,但是当我设置 TextFormField 的值时,我可以直接找到 EditableText 小部件。 这个小部件的我可以找到测试属性 maxLines。

    final EditableText formfield =
       tester.widget<EditableText>(find.text('testvalue'));
    
    expect(formfield.maxLines, 2);
    

    【讨论】:

    • 这个测试是一个变化检测器测试,它没有提供太多价值。我建议您在驱动程序测试中做一个屏幕截图,以确保您的 UI 状态良好
    • 这个例子很简单,因为我不想麻烦你。
    【解决方案2】:

    您看不到maxLines 或maxLength 等属性的原因是它们属于TextField 类。

    查看源文件中TextFormField构造函数的文档:

      /// Creates a [FormField] that contains a [TextField].
      ///
      /// When a [controller] is specified, [initialValue] must be null (the
      /// default). If [controller] is null, then a [TextEditingController]
      /// will be constructed automatically and its `text` will be initialized
      /// to [initialValue] or the empty string.
      ///
      /// For documentation about the various parameters, see the [TextField] class
      /// and [new TextField], the constructor.
    

    很遗憾,您无法从TextFormField 中检索TextField 对象,而必须通过查找器找到TextField 对象。

    假设您有一个包含 2 个字段的表单 - 名字和姓氏。您需要做的是找到所有TextField 类型的小部件,将它们添加到列表中,然后您可以遍历列表中的每个元素并运行测试。这是一个例子:

      testWidgets('Form fields have the correct maximum length and number of lines',
          (WidgetTester tester) async {
        await tester.pumpWidget(MaterialApp(
          home: Scaffold(
            body: Form(
              child: Column(
                children: <Widget>[
                  TextFormField(
                    key: Key('first_name'),
                    decoration: InputDecoration(hintText: 'First name'),
                    maxLines: 1,
                    maxLength: 50,
                    obscureText: true,
                  ),
                  TextFormField(
                    key: Key('last_name'),
                    decoration: InputDecoration(hintText: 'Last name'),
                    maxLines: 1,
                    maxLength: 25,
                  ),
                ],
              ),
            ),
          ),
        ));
    
        List<TextField> formFields = List<TextField>();
    
        find.byType(TextField).evaluate().toList().forEach((element) {
          formFields.add(element.widget);
        });
    
        formFields.forEach((element) {
          expect(element.maxLines, 1);
    
          switch (element.decoration.hintText) {
            case 'First name':
              expect(element.maxLength, 50);
              break;
    
            case 'Last name':
              expect(element.maxLength, 25);
              break;
          }
        });
      });
    

    如果你只有一个字段,你可以这样做:

    TextField textField = find.byType(TextField).evaluate().first.widget as TextField;
    
    expect(textField.maxLines, 1);
    expect(textField.maxLength, 50);
    

    【讨论】:

    • 这就是我在我的解决方案中所做的,但你描述得更好!
    【解决方案3】:

    您可以使用集成测试对其进行测试。逻辑是在 TextFormField 中输入预期的更多文本。
    因此我们可以验证 TextFormField 只允许 2 个字符,如下所示。
    例如组件:

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:font_awesome_flutter/font_awesome_flutter.dart';
    
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            body: SingleChildScrollView(
              child: MyLoginPage(title: 'Flutter Demo Home Page'),
            ),
          ),
        );
      }
    }
    
    class MyLoginPage extends StatefulWidget {
      MyLoginPage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyLoginPageState createState() => _MyLoginPageState();
    }
    
    class _MyLoginPageState extends State<MyLoginPage> {
      String _email;
      String _password;
      TextStyle style = TextStyle(fontSize: 25.0);
    
      @override
      Widget build(BuildContext context) {
        final emailField = TextField(
          key: Key('textformfield'),
          obscureText: false,
          maxLength: 2,
          style: style,
          decoration: InputDecoration(
              contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
              prefixIcon: Icon(FontAwesomeIcons.solidEnvelope),
              hintText: "Email",
              focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Colors.red[300], width: 32.0),
                  borderRadius: BorderRadius.circular(97.0))),
          onChanged: (value) {
            setState(() {
              _email = value;
            });
          },
        );
        final passwordField = TextField(
          obscureText: true,
          style: style,
          decoration: InputDecoration(
              contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
              prefixIcon: Icon(FontAwesomeIcons.key),
              hintText: "Password",
              focusedBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Colors.red[300], width: 32.0),
                  borderRadius: BorderRadius.circular(25.0))),
          onChanged: (value) {
            setState(() {
              _password = value;
            });
          },
        );
    
        return Center(
          child: Column(
            children: <Widget>[
              Container(
                color: Colors.yellow[300],
                height: 300.0,
              ),
              emailField,
              passwordField,
            ],
          ),
        );
      }
    }
    

    测试:

    import 'package:flutter/material.dart';
    import 'package:flutter_test/flutter_test.dart';
    
    import 'package:flutter_textfields_up/main.dart';
    
    void main() {
      testWidgets('Email should be only 2 characters', (WidgetTester tester) async {
        // Build our app and trigger a frame.
        await tester.pumpWidget(MyApp());
        var txtForm = find.byKey(Key('textformfield'));
        await tester.enterText(txtForm, '123');
    
        expect(find.text('123'), findsNothing); // 3 characters shouldn't be allowed
        expect(find.text('12'), findsOneWidget); // 2 character are valid.
    
      });
    }
    

    请注意我发送 3 个字符,而 TextFormField 应该只允许 2 个。
    希望对您有所帮助。

    【讨论】:

    • 不,我想不想测试我想测试 maxLines 属性的值
    猜你喜欢
    • 2022-10-18
    • 2021-07-23
    • 2012-01-08
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-30
    • 2019-01-05
    相关资源
    最近更新 更多