Approval Testing with Widget tests | Flutter / Dart 🎯
Tags: dart, flutter, testing, approval
Published: 2024-07-03 07:15:23

Hi there, I already wrote an article about [ApprovalTests for unit tests](https://dev.to/yelmuratoff/approval-testing-and-why-its-important-dart-3pic).
In this article we will touch on Widget tests and how this package can be useful for us.

## Let's briefly go through again what exactly are ApprovalTests?

In manual code testing, developers usually write automated tests to guard against regressions, specifying the expected outcomes directly in the code. `ApprovalTests` uses a similar method but stores the expected outcomes in a file instead. This file is generated by the test library rather than the developer, making the process of writing and maintaining tests more efficient. As a result, developers can spend more time on the functional code rather than the test code.

## 📋 How it works step by step

- The first run of the test automatically creates an `approved` file if there is no such file.
- If the changed results match the `approved` file perfectly, the test passes.
- If there's a difference, a `reporter` tool will highlight the mismatch and the test fails.
- If the test is passed, the `received` file is deleted automatically. You can change this by changing the `deleteReceivedFile` value in `options`. If the test fails, the received file remains for analysis.

Instead of writing:
```dart
    testWidgets('home page', (WidgetTester tester) async {
        await tester.pumpWidget(const MyApp());
        await tester.pumpAndSettle();

        expect(find.text('You have pushed the button this many times:'), findsOneWidget);
        expect(find.text('0'), findsOneWidget);
        expect(find.byWidgetPredicate(
            (Widget widget) => widget is Text && widget.data == 'hello' && 
            widget.key == ValueKey('myKey'),
        ), findsOneWidget);
        expect(find.text('Approved Example'), findsOneWidget);
    });
```

Write this:
```dart
    testWidgets('home page', (WidgetTester tester) async {
        await tester.pumpWidget(const MyApp());
        await tester.pumpAndSettle();

        await tester.approvalTest();
    });
```

To include your project's custom widget types in your test, and to perform post-test checks, add calls to `Approved.setUpAll()` to your tests' `setUpAll` calls, like so:

```dart
    main() {
        setUpAll(() {
            Approved.setUpAll();
        });
    }
```

And the result will be something like this:

![Flutter example result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msitbtlk6rw18zccdwgk.png)

![Console log result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88m1vm3eddakiy4o6690.png)

But if we for example change something and run the text, it will show us the difference, where it differs. There are several types of `Reporter` in the project, but the standard is `CommandLineReporter`, which gives the difference on the command line. Other reporters can be found in the project's Github repository.

![CommandLineReporter example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx6ogxdxu7azbi02ldt6.png)


## Approving Results

Approving results just means saving the `.approved.txt` file with your desired results.

We’ll provide more explanation in due course, but, briefly, here are the most common approaches to do this.

#### • Via Diff Tool
Most diff tools have the ability to move text from left to right, and save the result.
How to use diff tools is just below, there is a `Comparator` class for that.

#### • Via CLI command
You can run the command in a terminal to review your files:
```bash
dart run approval_tests:review
```
After running the command, the files will be analyzed and you will be asked to choose one of the options:
- `y` - Approve the received file.
- `n` - Reject the received file.
- `v`iew - View the differences between the received and approved files. After selecting `v` you will be asked which IDE you want to use to view the differences.

The command `dart run approval_tests:review` has additional options, including listing files, selecting
 files to review from this list by index, and more. For its current capabilities, run 
```bash
dart run approval_tests:review --help
```

#### • Via approveResult property
If you want the result to be automatically saved after running the test, you need to use the `approveResult` property in `Options`:

```dart
void main() {
  test('test JSON object', () {
    final complexObject = {
      'name': 'JsonTest',
      'features': ['Testing', 'JSON'],
      'version': 0.1,
    };

    Approvals.verifyAsJson(
      complexObject,
      options: const Options(
        approveResult: true,
      ),
    );
  });
}
```

this will result in the following file
`example_test.test_JSON_object.approved.txt`
```txt
{
  "name": "JsonTest",
  "features": [
    "Testing",
    "JSON"
  ],
  "version": 0.1
}
```

#### • Via file rename
You can just rename the `.received` file to `.approved`.

## ❓ Which File Artifacts to Exclude from Source Control
You must add any `approved` files to your source control system. But `received` files can change with any run and should be ignored. For Git, add this to your `.gitignore`:

```gitignore
*.received.*
```

Show some 💙 and star the [repo](https://github.com/approvals/ApprovalTests.Dart) to support the project! 🫰

For any questions, feel free to reach out via [Telegram](https://t.me/yelmuratoff) or email at [yelaman.yelmuratov@gmail.com](mailto:yelamanyelmuratov@gmail.com).