简体   繁体   中英

How to unit Test a Flutter TextFormField maxlines

Is it possible to write a unit test that verifies that the maxLines property of a TextFormField is set correctly. I can not find a way to access the property:

i create a TextFormField

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

then in the test i get access to the form field with tester.widget

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

but since the maxLines property is passed to the Builder which returns a Textfield, how can i get access to the textfield.

Or is there an completely other ways to verify this?

I don't know if this is a good solution but as i set the value of my TextFormField i can find the EditableText widget directly. of this widget i can find test the property maxLines.

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

expect(formfield.maxLines, 2);

The reason you can't see properties such as maxLines or maxLength is because they belong to the TextField class.

Take a look at the documentation of the TextFormField constructor in the source file:

  /// 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.

Unfortunately you can't retrieve the TextField object from a TextFormField , you'll have to find the TextField object through a finder instead.

Let's assume you have a form with 2 fields - first name and last name. What you need to do is find all widgets of type TextField , add them to a list and then you can loop through each element of the list and run your test. Here's an example:

  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;
      }
    });
  });

If you only one field, you could do this instead:

TextField textField = find.byType(TextField).evaluate().first.widget as TextField;

expect(textField.maxLines, 1);
expect(textField.maxLength, 50);

You can test it with an Integration test. The logic would be to type more text into the TextFormField that the one it's expected.
So we can verify the TextFormField is only allowing 2 character, as follow.
eg component:

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,
        ],
      ),
    );
  }
}

The test:

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.

  });
}

Please observe I'm sending 3 character, and the TextFormField should only allow 2.
Hope this help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM