Skip to content

Commit

Permalink
- added testing guide on golden and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ps9310 committed Jun 11, 2024
1 parent c590253 commit b3b6425
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 0 deletions.
121 changes: 121 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,127 @@ For more information on widgetbook, [read the docs](https://docs.widgetbook.io/)

We should also create a test for each widget created.

### Contributing Guide for Writing Tests

#### Folder Structure

To maintain consistency and ease of navigation, follow the same folder structure in the `test` directory as in the `lib` directory. For example, if you have a file located at `lib/src/components/tooltip/tooltip.dart`, your test file should be located at `test/src/components/tooltip/tooltip_test.dart`.

##### Example Folder Structure

```
lib/
└── src/
└── components/
└── tooltip/
└── tooltip.dart
test/
└── src/
└── components/
└── tooltip/
└── tooltip_test.dart
```

#### Writing Tests

1. **Unit Tests**: Test individual functions and classes.
2. **Widget Tests**: Test individual widgets and their interactions.
3. **Integration Tests**: Test the complete app or large parts of it.

##### Guidelines

- Use descriptive test names.
- Test one thing per test.
- Mock dependencies using `mockito`.
- Write tests for edge cases.

#### Measuring Code Coverage

To ensure high code coverage (at least > 96%), use the following steps:

##### With 'lcov'
1. **Run the script coverage.sh from the project root, which make use of `lcov` to generate the coverage report**:
```sh
sh coverage.sh
```
##### Alternatively, with 'genhtml'
1. **Install dependencies**:
```sh
flutter pub add --dev test coverage
```

2. **Run tests with coverage**:
```sh
flutter test --coverage
```

3. **Generate coverage report**:
```sh
genhtml coverage/lcov.info -o coverage/html
```

4. **View coverage report**:
Open `coverage/html/index.html` in a web browser.


##### Maximizing Coverage

- Write tests for all public methods and classes.
- Include tests for edge cases and error handling.
- Avoid excluding files from coverage unless necessary.

#### Golden Tests

Golden tests are used to ensure that the visual output of your widgets remains consistent over time. They are particularly useful for complex UI components.

##### Why Golden Tests?

In the `tooltip` example, the direction of the arrows in the tooltip is crucial. Golden tests help to ensure that any changes to the tooltip do not unintentionally alter the direction of the arrows or other visual aspects.

##### Adding Golden Tests

1. **Set up golden tests**:
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:zeta_flutter/zeta_flutter.dart';
import '../../../test_utils/test_app.dart';
void main() {
testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
arrowDirection: ZetaTooltipArrowDirection.up,
child: Text('Tooltip up'),
),
),
),
);
expect(find.text('Tooltip up'), findsOneWidget);
// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_up.png')),
);
});
}
```

2. **Run golden tests**:
```sh
flutter test --update-goldens
```

3. **Verify golden tests**:
Ensure that the generated golden files are correct and commit them to the repository.



## Code reviews

All submissions, including submissions by project members, require review. We use GitHub pull requests (PRs) for this purpose. Consult [GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for more information on using pull requests.
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.4.4
path: ^1.9.0
zds_analysis: ^1.0.0

flutter:
Expand Down
Binary file added test/src/components/tooltip/golden/arrow_down.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/src/components/tooltip/golden/arrow_left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/src/components/tooltip/golden/arrow_up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 208 additions & 0 deletions test/src/components/tooltip/tooltip_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:zeta_flutter/zeta_flutter.dart';

import '../../../test_utils/test_app.dart';

void main() {
group('ZetaTooltip Widget Tests', () {
testWidgets('renders with default properties', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
child: Text('Tooltip text'),
),
),
),
);

expect(find.text('Tooltip text'), findsOneWidget);
expect(find.byType(ZetaTooltip), findsOneWidget);
});

testWidgets('renders with custom color and padding', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
color: Colors.red,
padding: EdgeInsets.all(20),
child: Text('Tooltip text'),
),
),
),
);

final tooltipBox = tester.widget<DecoratedBox>(
find.descendant(
of: find.byType(ZetaTooltip),
matching: find.byType(DecoratedBox),
),
);

expect((tooltipBox.decoration as BoxDecoration).color, Colors.red);

final padding = tester.widget<Padding>(
find.descendant(
of: find.byType(ZetaTooltip),
matching: find.byType(Padding),
),
);

expect(padding.padding, const EdgeInsets.all(20));
});

testWidgets('renders with custom text style', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
textStyle: TextStyle(fontSize: 24, color: Colors.blue),
child: Text('Tooltip text'),
),
),
),
);

// Find the RichText widget, which is a descendant of the Text widget
final richTextFinder = find.descendant(
of: find.text('Tooltip text'),
matching: find.byType(RichText),
);

expect(richTextFinder, findsOneWidget);

// Verify the styles
final RichText richTextWidget = tester.widget(richTextFinder);
final TextSpan textSpan = richTextWidget.text as TextSpan;

expect(textSpan.style?.fontSize, 24);
expect(textSpan.style?.color, Colors.blue);
});

testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
arrowDirection: ZetaTooltipArrowDirection.up,
child: Text('Tooltip up'),
),
),
),
);

expect(find.text('Tooltip up'), findsOneWidget);

// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_up.png')),
);
});

testWidgets('renders with arrow correctly in down direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
child: Text('Tooltip down'),
),
),
),
);

expect(find.text('Tooltip down'), findsOneWidget);

// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_down.png')),
);
});

testWidgets('renders with arrow correctly in left direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
arrowDirection: ZetaTooltipArrowDirection.left,
child: Text('Tooltip left'),
),
),
),
);

expect(find.text('Tooltip left'), findsOneWidget);

// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_left.png')),
);
});

testWidgets('renders with arrow correctly in right direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
arrowDirection: ZetaTooltipArrowDirection.right,
child: Text('Tooltip right'),
),
),
),
);

expect(find.text('Tooltip right'), findsOneWidget);

// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_right.png')),
);
});

testWidgets('renders with rounded and sharp corners', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: Column(
children: [
ZetaTooltip(
child: Text('Rounded tooltip'),
),
ZetaTooltip(
rounded: false,
child: Text('Sharp tooltip'),
),
],
),
),
),
);

expect(find.text('Rounded tooltip'), findsOneWidget);
expect(find.text('Sharp tooltip'), findsOneWidget);

final roundedTooltipBox = tester.widget<DecoratedBox>(
find.descendant(
of: find.widgetWithText(ZetaTooltip, 'Rounded tooltip'),
matching: find.byType(DecoratedBox),
),
);

final sharpTooltipBox = tester.widget<DecoratedBox>(
find.descendant(
of: find.widgetWithText(ZetaTooltip, 'Sharp tooltip'),
matching: find.byType(DecoratedBox),
),
);

expect((roundedTooltipBox.decoration as BoxDecoration).borderRadius, ZetaRadius.minimal);
expect((sharpTooltipBox.decoration as BoxDecoration).borderRadius, null);
});
});
}

0 comments on commit b3b6425

Please sign in to comment.