Testing Guide
- Zeta Flutter
- Zeta Web
Directory structure
Test files are stored in the top-level tests
folder. Each test file should replicate the directory structure of the corresponding component in the lib
directory to ensure consistency and easy navigation.
For example, if your component is located at lib/src/components/buttons/button.dart
, the test should be placed at test/src/components/buttons/button_test.dart
.
Test groups
Each test file should follow a consistent pattern, organizing tests into groups for Accessibility, Content, Dimensions, Styling, Interaction, Golden, and Performance. While not every group needs to be populated for each component, they should be included as necessary to thoroughly validate the component's behavior and quality.
-
Accessibility Tests
Semantic labels, touch areas, contrast ratios, etc. -
Content Tests
Finds the widget, parameter statuses, etc.
Checking for the value of props and attributes of the widget. Checking for the presence of widgets. -
Dimensions Tests
Size, padding, margin, alignment, etc.
getSize(). -
Styling Tests
Rendered colors, fonts, borders, radii etc. Checking the style of widgets and child widgets. -
Interaction Tests
Gesture recognizers, taps, drags, etc. For example, using a boolean to check if the widgets interaction function runs. -
Golden Tests
Compares the rendered widget with the golden file. Use thegoldenTest()
function from test_utils/utils.dart. -
Performance Tests
Animation performance, rendering performance, data manipulation performance, etc.
Testing file template
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zeta_flutter/zeta_flutter.dart';
import '../../../test_utils/test_app.dart';
import '../../../test_utils/tolerant_comparator.dart';
import '../../../test_utils/utils.dart';
void main() {
const String parentFolder = 'ENTER_PARENT_FOLDER (e.g. button)';
const goldenFile = GoldenFiles(component: parentFolder);
setUpAll(() {
goldenFileComparator = TolerantComparator(goldenFile.uri);
});
group('Accessibility Tests', () {});
group('Content Tests', () {
final debugFillProperties = {
'': '',
};
debugFillPropertiesTest(
widget,
debugFillProperties,
);
});
group('Dimensions Tests', () {});
group('Styling Tests', () {});
group('Interaction Tests', () {});
group('Golden Tests', () {
goldenTest(goldenFile, widget, 'PNG_FILE_NAME');
});
group('Performance Tests', () {});
}
Golden testing
Golden testing is a technique in software development that verifies the visual appearance of UI components by capturing a “golden” image of the component in its ideal state. This image serves as a reference for future tests, allowing any visual changes or regressions to be easily identified.
When conducting golden tests, aim to create tests for each unique variation of a component, including:
- Different states (e.g., active, disabled)
- Various themes or styles
- Different sizes or configurations
This approach ensures that all possible visual appearances of the component are captured and can be verified for consistency.
For Flutter golden tests, set up the testing framework as follows:
-
Initialize the Golden File Reference: Define the reference for the golden file (as shown in line 4 below).
-
Set Up a Custom Comparator: Use lines 6-8 to configure a custom comparator, which compares the saved images to the rendered output. This setup remains consistent across all tests.
-
Use the goldenTest Helper Method: Lines 12-19 demonstrate how to utilize this method, which requires the golden file reference, the code to render, and the test file’s name.
Before executing the tests, run flutter test --update-goldens
within your test directory to populate the golden directory; you should only do this for newly created tests. This command ensures your golden images are up-to-date and reflect the intended visual state.
void main(){
const String parentFolder = 'componentName'; // i.e. Button
const goldenFile = GoldenFiles(component: parentFolder);
setUpAll(() {
goldenFileComparator = TolerantComparator(goldenFile.uri);
});
//...
group('Golden Tests', () {
goldenTest(
goldenFile,
const ZetaComponentName(prop1: '1', prop2: '2'),
'component_name_prop11_prop22',
);
// Create cases for all variances
});
}
Helper functions
As you are writing tests think about helper function you could write and add them to the test_utils/utils.dart
file. This will help you and others write tests faster and more consistently.
- For golden tests
goldenTest(GoldenFiles goldenFile, Widget widget, Type widgetType, String fileName, {bool darkMode = false})
- For debugFillProperties tests
debugFillPropertiesTest(Widget widget, Map<String, dynamic> debugFillProperties)
Guidelines
- Use descriptive test names.
- Test one thing per test.
- Mock dependencies using
mockito
. - Write tests for edge cases.
Directory structure
Test files are stored in the src/tests
folder. Each test file should replicate the directory structure of the corresponding component in the src/components
directory to ensure consistency and easy navigation.
For example, if your component is located at src/components/button/button.ts
, the test should be placed at src/test/button/button.test.ts
.
Test groups
Each test file should follow a consistent pattern, organizing tests into groups for Accessibility, Content, Dimensions, Styling, Interaction, Golden, and Performance. While not every group needs to be populated for each component, they should be included as necessary to thoroughly validate the component's behavior and quality.
-
Accessibility
Semantic labels, touch areas, contrast ratios, etc. -
Content
Finds the component, parameter statuses, etc.
Checking for the value of props and attributes of the component. Checking for the presence of sub-element. -
Dimensions
Size, padding, margin, alignment, etc. -
Styling
Rendered colors, fonts, borders, radii etc. Checking the style of elements and child elements. -
Interaction
Gesture recognizers, taps, drags, etc. For example, using a boolean to check if the elements interaction function runs. -
Golden
Compares the rendered component with the golden file. -
Performance
Animation performance, rendering performance, data manipulation performance, etc.
Testing file template
import { fixture, html, expect, unsafeStatic } from "@open-wc/testing";
import type { Zeta_replacecap_ } from "PATH_TO_COMPONENT";
import "PATH_TO_COMPONENT";
import "../../index.css";
describe("zeta-_replacelower_", () => {
let subject: Zeta_replacecap_;
const createComponent = (template = `<zeta-_replacelower_></zeta-_replacelower_>`) => {
// prettier-ignore
return fixture<Zeta_replacecap_>(html`${unsafeStatic(template)}`);
};
beforeEach(async () => {
subject = await createComponent();
});
describe("Accessibility", () => {
it("meets accessibility requirements", async () => {
await expect(subject).shadowDom.to.be.accessible();
});
});
// describe("Content", () => {});
// describe("Dimensions", () => {});
// describe("Styling", () => {});
// describe("Interaction", () => {});
// describe("Golden", () => {});
// describe("Performance", () => {});
});
Golden testing
Golden testing is not currently implemented in Zeta Web; however, it is a feature we are actively working on and plan to introduce in the near future to enhance our testing capabilities.
Helper functions
To streamline and enhance the testing process, we’ve developed a set of utility functions designed to handle common testing tasks efficiently. These functions simplify operations such as converting CSS variables to their computed values, retrieving slotted elements, managing coordinates, and executing various mouse interactions. By leveraging these utilities, developers can focus on writing more meaningful tests without getting bogged down by repetitive tasks.
See utils.ts for all utility functions.
Guidelines
- Test group describe blocks must be nested in "component" describe blocks
describe("replace-with-zeta-tag", () => {
. - You can have multiple "component" describe blocks.
- Aim to keep nesting to a minimum. Use only the "component" and "category" describe blocks. Do not nest any further.
- Comment out unused describe blocks.
- You may need to reinitialize the component within the nested category describe block if you find peculiar results.
Put this at the top of the troublesome category describe block.
beforeEach(async () => {
subject = await createComponent();
});