Flutter Testing : Authentication Flow
Tags: flutter, testing, fluttertesting, dart
Published: 2023-09-03 09:31:55

This document provides a comprehensive example of testing an authentication flow using the AuthForm widget. We'll cover unit testing for validation logic, widget testing for UI interactions, and integration testing for the entire authentication process.

[Auth-Form Widget](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/lib/main.dart)

The AuthForm widget is a form for user authentication. It includes input fields for email and password, along with validation logic and a submit button.

```
class AuthForm extends StatefulWidget {
  const AuthForm({super.key});

  @override
  State<AuthForm> createState() => _AuthFormState();
}

class _AuthFormState extends State<AuthForm> {
  @override
  Widget build(BuildContext context) {
    AuthLogic authLogic = AuthLogic();
    return Scaffold(
      appBar: AppBar(
        title: const Text("Testing example"),
      ),
      body: Form(
        key: authLogic.formKey,
        child: Column(
          children: [
            TextFormField(
              key: const Key('emailField'),
              controller: authLogic.emailController,
              validator: authLogic.emailValidator,
              decoration: const InputDecoration(labelText: 'Email'),
            ),
            TextFormField(
              key: const Key('passwordField'),
              controller: authLogic.passwordController,
              validator: authLogic.passwordValidator,
              decoration: const InputDecoration(labelText: 'Password'),
            ),
            ElevatedButton(
              key: const Key('submitButton'),
              onPressed: authLogic.submitForm,
              child: const Text('Submit'),
            ),
          ],
        ),
      ),
    );
  }
}
```
[Auth-Form Logic](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/lib/logic.dart)

```
import 'package:flutter/material.dart';

class AuthLogic {
  final formKey = GlobalKey<FormState>();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  String? emailValidator(String? value) {
    if (value == null || value.isEmpty || !value.contains('@')) {
      return 'Invalid email';
    }
    return null;
  }

  String? passwordValidator(String? value) {
    if (value == null || value.isEmpty || value.length < 6) {
      return 'Password must be at least 6 characters';
    }
    return null;
  }

  void submitForm() {
    if (formKey.currentState!.validate()) {
      final email = emailController.text;
      final password = passwordController.text;

      // Perform authentication logic
      if (email == 'test@example.com' && password == 'password123') {
        // If credentials are valid, you can trigger a success action here
        // For now, let's print a success message
        print('Authentication successful');
      } else {
        // If credentials are invalid, you can trigger a failure action here
        // For now, let's print an error message
        print('Authentication failed');
      }
    }
  }
}

```
main.dart

```
import 'package:auth_example/logic.dart';
import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';

void main() {
  enableFlutterDriverExtension();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Testing Example'
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const AuthForm(),
    );
  }
}

```

[Dev dependencies](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/pubspec.yaml)
```

dev_dependencies:
  flutter_driver:
    sdk: flutter
  flutter_test:
    sdk: flutter
```

[Unit Test](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/test/auth_form_unit_test.dart)

Unit tests focus on testing individual components of the AuthForm widget, specifically the validation logic for email and password fields.

```
import 'package:flutter_test/flutter_test.dart';
import 'package:auth_example/auth_form.dart';

void main() {
  group('AuthForm Unit Tests', () {
    // Test for a valid email address
    test('Valid email', () {
      // Arrange: Create a validator function for email validation
      final validator = AuthForm()._emailValidator;

      // Act: Invoke the validator with a valid email
      final result = validator('test@example.com');

      // Assert: Ensure the result is null, indicating a valid email
      expect(result, null);
    });

    // Test for an invalid email address
    test('Invalid email', () {
      // Arrange: Create a validator function for email validation
      final validator = AuthForm()._emailValidator;

      // Act: Invoke the validator with an invalid email
      final result = validator('invalid_email');

      // Assert: Ensure the result is 'Invalid email', indicating an error
      expect(result, 'Invalid email');
    });

    // Test for a valid password
    test('Valid password', () {
      // Arrange: Create a validator function for password validation
      final validator = AuthForm()._passwordValidator;

      // Act: Invoke the validator with a valid password
      final result = validator('password123');

      // Assert: Ensure the result is null, indicating a valid password
      expect(result, null);
    });

    // Test for an invalid password
    test('Invalid password', () {
      // Arrange: Create a validator function for password validation
      final validator = AuthForm()._passwordValidator;

      // Act: Invoke the validator with an invalid password
      final result = validator('123');

      // Assert: Ensure the result is 'Password must be at least 6 characters', indicating an error
      expect(result, 'Password must be at least 6 characters');
    });
  });
}

```

[Widget Testing](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/test/widget_test.dart) 

Widget tests verify the behavior of the AuthForm widget's UI interactions, such as entering data and submitting the form.

```
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:auth_example/auth_form.dart';

void main() {
  testWidgets('AuthForm UI Test', (WidgetTester tester) async {
    // Arrange: Build the widget under test, which is the AuthForm, within a MaterialApp.
    await tester.pumpWidget(MaterialApp(home: AuthForm()));

    // Find the email, password, and submit button widgets using their Key identifiers.
    final emailField = find.byKey(Key('emailField'));
    final passwordField = find.byKey(Key('passwordField'));
    final submitButton = find.byKey(Key('submitButton'));

    // Act: Enter text into the email and password fields.
    await tester.enterText(emailField, 'test@example.com');
    await tester.enterText(passwordField, 'password123');

    // Tap the submit button to trigger the form submission.
    await tester.tap(submitButton);
    await tester.pump(); // Complete the frame transition.

    // Assert: Check that the text 'Form submitted' is found in the widget tree.
    expect(find.text('Form submitted'), findsOneWidget);
  });
}


```
[Integration Test ](https://github.com/mrtarunsaraswat/flutter-testing/blob/main/test/auth_form_integration_test.dart)

Integration tests simulate the entire authentication flow, combining multiple components and interactions.

```
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_test/flutter_test.dart' as test;

void main() {
  test.group('AuthForm Integration Test', () {
    FlutterDriver? driver;

    // Connect to the app before running tests
    test.setUpAll(() async {
      // Establish a connection to the Flutter app running on a device or emulator.
      driver = await FlutterDriver.connect(
        // dartVmServiceUrl is a URL used for connecting to the Dart VM service,
        // which is a debugging and profiling service provided by the Dart runtime.
        dartVmServiceUrl: "ws://127.0.0.1:58557/lTla2qH-HXs=/ws",
        printCommunication: true, // Print communication logs for debugging.
      );
    });

    // Close the connection after tests are done
    test.tearDownAll(() async {
      if (driver != null) {
        // Close the FlutterDriver connection.
        driver?.close();
      }
    });

    // Test for successful authentication
    test.test('Successful authentication', () async {
      // Find email, password, and submit button widgets using their value keys.
      final emailField = find.byValueKey('emailField');
      final passwordField = find.byValueKey('passwordField');
      final submitButton = find.byValueKey('submitButton');

      // Tap the email field and enter a valid email.
      await driver?.tap(emailField);
      await driver?.enterText('test@example.com');

      // Tap the password field and enter a valid password.
      await driver?.tap(passwordField);
      await driver?.enterText('password123');

      // Tap the submit button to simulate a form submission.
      await driver?.tap(submitButton);
    }, timeout: test.Timeout.none); // No timeout for this test.

    // Test for failed authentication
    test.test('Failed authentication', () async {
      // Find email, password, and submit button widgets using their value keys.
      final emailField = find.byValueKey('emailField');
      final passwordField = find.byValueKey('passwordField');
      final submitButton = find.byValueKey('submitButton');

      // Tap the email field and enter a valid email.
      await driver?.tap(emailField);
      await driver?.enterText('test@example.com');

      // Tap the password field and enter an invalid password.
      await driver?.tap(passwordField);
      await driver?.enterText('123'); // Invalid password

      // Tap the submit button to simulate a form submission.
      await driver?.tap(submitButton);
    }, timeout: test.Timeout.none); // No timeout for this test.
  });
}


```

This detailed documentation provides a structured overview of each testing aspect for the "auth_example" app, including the AuthForm widget, unit tests, widget tests, and integration tests. You can use this documentation as a reference to understand and implement testing strategies in your Flutter app.