Dart and Flutter reference
These are the main building blocks you can use to integrate Dart and Flutter tests with Allure by using allure_dart_test and allure_flutter_test.
Drop-in test wrappers
The drop-in libraries replace the declaration functions of the underlying framework and re-export everything else unchanged:
package:allure_dart_test/test.dartreplacestest,group,setUp,tearDown,setUpAll, andtearDownAllfrompackage:test,package:allure_flutter_test/flutter_test.dartadditionally replacestestWidgetsfromflutter_test,package:allure_flutter_test/integration_test.dartprovides the same wrappers plus theintegration_testexports, includingIntegrationTestWidgetsFlutterBinding.
The wrappers accept the same arguments as the originals. On top of the original behavior, they:
- report each test as an Allure result with automatic labels derived from the file path and group hierarchy,
- parse inline metadata markers from test names,
- report
setUp,tearDown,setUpAll, andtearDownAllcallbacks as Allure fixtures, - declaration-skip tests excluded by an Allure test plan (no Allure result is written).
testWidgets and variants
When a widget test uses a TestVariant (for example, ValueVariant), the Allure wrapper reports each variant value as a separate test result:
testWidgets(
'renders both layouts',
(tester) async {
// ...
},
variant: ValueVariant<String>({'compact', 'expanded'}),
);Each result is named renders both layouts (variant: compact) and renders both layouts (variant: expanded), carries a variant parameter with the value, and shares the renders both layouts test case name. Test plan selectors match the variant-specific full names.
Auto screenshot and golden-diff attachments
allure_flutter_test's installAllure() — not the allure_dart_test one — accepts two Flutter-only flags:
installAllure(autoScreenshotOnFailure: true)— on a failing test, captures a screenshot of the widget tree and attaches it asscreenshot-on-failure(PNG).installAllure(autoAttachGoldenDiff: true)— on amatchesGoldenFilemismatch, attaches the actual rendered image asgolden-actual, plus, best-effort (when the comparator is the defaultLocalFileComparator), whichever ofgolden-masterImage,golden-testImage,golden-maskedDiff, andgolden-isolatedDiffit already wrote to disk. The comparator only computes a pixel diff when the actual and golden images are the same size, so a dimension mismatch attaches just the master and test images, while a same-size pixel mismatch also attaches the two diff visualizations.
Both flags install a process-wide hook the first time either is passed as true, are best-effort (a capture failure never masks the original test failure), and are monotonic — once enabled, a later bare installAllure() call in the same process does not disable them:
import 'dart:async';
import 'package:allure_flutter_test/allure_flutter_test.dart';
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
installAllure(autoScreenshotOnFailure: true, autoAttachGoldenDiff: true);
await testMain();
}You can still attach screenshots or golden files manually with the regular attachment APIs, whether or not these hooks are enabled.
Runtime API
The runtime functions are exported by package:allure_dart_test/allure_dart_test.dart and package:allure_flutter_test/allure_flutter_test.dart. They apply to the currently running test — or, when called inside a fixture, to the fixture and its scope. All of them are asynchronous and should be awaited. Import the library with a prefix to keep the short names readable:
import 'package:allure_dart_test/allure_dart_test.dart' as allure;Metadata and labels
allure.displayName(name)— overrides the test name shown in the reportallure.description(markdown)allure.descriptionHtml(html)allure.label(name, value)allure.labels([AllureLabel(...), ...])allure.allureId(value)allure.owner(value)allure.severity(value)allure.layer(value)allure.tag(value)allure.tags(['smoke', 'auth'])allure.testCaseName(value)allure.testCaseId(value)allure.historyId(value)
Example:
test('login works', () async {
await allure.description('Checks that a valid user can sign in.');
await allure.owner('John Doe');
await allure.severity('critical');
await allure.label('microservice', 'ui');
await allure.tags(['smoke', 'auth']);
});Hierarchies
allure.epic(value)allure.feature(value)allure.story(value)allure.parentSuite(value)allure.suite(value)allure.subSuite(value)
Links and parameters
allure.link(url, name: ..., type: ...)allure.links([AllureLink(...), ...])allure.issue(url, name: ...)allure.tms(url, name: ...)allure.parameter(name, value, excluded: ..., mode: ...)
A parameter value can be any object; non-string values are JSON-encoded. Parameters with excluded: true don't affect the test's history ID. The mode argument controls rendering: AllureParameterMode.masked hides the value behind asterisks, and AllureParameterMode.hidden omits the parameter from the report.
test('login works', () async {
await allure.issue('https://jira.example.com/browse/AUTH-123', name: 'AUTH-123');
await allure.parameter('browser', 'firefox');
await allure.parameter('password', 'qwerty', mode: allure.AllureParameterMode.masked);
});Statuses
allure.statusDetails(message: ..., trace: ..., known: ..., muted: ..., flaky: ..., actual: ..., expected: ...)allure.markKnown()allure.markMuted()allure.markFlaky()
A test's status is resolved automatically: assertion failures (such as failed expect matchers) are reported as failed, any other errors as broken, and skipped tests (for example, via markTestSkipped, or a declaration-time skip: on the drop-in wrappers, which is converted into a runtime self-skip) as skipped. The one exception is a declaration-time skip: with installAllure() and the original framework imports: the framework never runs setUp for a declaration-skipped test, which is where that style schedules the result, so no result is produced at all — see Skip tests for the full breakdown, including the group-skip fixture tradeoff. For matcher failures, the actual and expected values are extracted into the status details automatically. The statusDetails call lets you add classification flags on top:
test('login works', () async {
await allure.statusDetails(flaky: true);
});Steps
allure.step(name, (step) async { ... })— runs the body inside a step and returns its valueallure.logStep(name, status: ..., error: ...)— records an instant, already-completed step
The step context object passed to the body provides:
step.displayName(name)— renames the current stepstep.parameter(name, value, mode: ...)— adds a parameter to the current step
test('login works', () async {
final session = await allure.step('create session', (step) async {
await step.parameter('user', 'alice');
return Session('alice');
});
await allure.logStep('cleanup skipped', status: allure.AllureStatus.skipped);
});Steps nest: a step call inside another step's body creates a child step. If the body throws, the step is marked failed or broken and the error is rethrown.
Attachments
allure.attachment(name, content, contentType: ..., fileExtension: ..., wrapInStep: ..., timestamp: ...)allure.attachmentPath(name, path, contentType: ..., fileExtension: ..., wrapInStep: ..., timestamp: ...)allure.attachTrace(name, path)— attaches an existing Playwright trace archive so the report opens it in the Trace Viewer (does not generate traces or depend on Playwright)
Attachment content can be a String, a list of bytes, or any JSON-encodable object. With wrapInStep: true (the default), the attachment is added as an attachment step, so it keeps its position between the surrounding steps.
WARNING
Inside a Flutter testWidgets body, real I/O attachment calls (attachmentPath, or attachment with stream content) must run inside tester.runAsync(...), or the test hangs — testWidgets runs on a fake-async zone that never pumps real I/O callbacks otherwise.
test('login works', () async {
await allure.attachment(
'response.json',
'{"status":"ok","user":"demo"}',
contentType: 'application/json',
fileExtension: 'json',
);
await allure.attachmentPath(
'screenshot',
'screenshots/failure.png',
contentType: 'image/png',
);
});Run-level attachments and errors
These write run-level data not tied to any single test — for example, a server log for the whole run or an infrastructure failure:
allure.globalAttachment(name, content, contentType: ..., fileExtension: ...)allure.globalAttachmentPath(name, path, contentType: ..., fileExtension: ...)allure.globalError(message: ..., trace: ..., known: ..., muted: ..., flaky: ..., actual: ..., expected: ...)
allureTest
allureTest from package:allure_dart_test/allure_dart_test.dart is an alternative to the drop-in wrappers for individual tests. It declares a package:test test whose body receives a context object with test-scoped helpers, including durable attachment APIs for large or late-produced artifacts:
import 'package:allure_dart_test/allure_dart_test.dart';
void main() {
allureTest('captures logs', (allure) async {
await allure.step('start server', () async {
// ...
});
await allure.streamAttachment(
name: 'server log',
type: 'text/plain',
extension: 'log',
content: logFile.openRead(),
);
});
}The context object provides:
step(name, body)— like the top-level step API, without the step context argumentlabel(name, value),parameter(name, value, ...),testCaseName(value),statusDetails(...)attachment(name: ..., type: ..., content: ..., extension: ..., wrapInStep: ...)— binary contenttextAttachment(name: ..., content: ..., type: ..., extension: ..., wrapInStep: ...)— text content,text/plainby defaultstreamAttachment(name: ..., content: ..., type: ..., extension: ..., wrapInStep: ...)— writes from a byte streampreparedAttachment(name: ..., type: ..., extension: ..., write: ..., wrapInStep: ...)— reserves an attachment file and lets your callback write it before the result references it
allureTest also accepts timeout, skip, and initial labels, parameters, and links lists.
Customizing the lifecycle
installAllure() accepts a custom AllureLifecycle, which is the place to set run-level report data:
import 'package:allure_dart_test/allure_dart_test.dart';
import 'package:test/test.dart';
void main() {
installAllure(
lifecycle: AllureLifecycle(
linkUrlTemplates: {
'issue': 'https://jira.example.com/browse/{}',
'tms': 'https://tms.example.com/cases/{}',
},
executorInfo: const AllureExecutorInfo(
name: 'GitHub Actions',
type: 'github',
buildName: 'Build #42',
),
environmentInfo: const {'stand': 'staging'},
categories: [
AllureCategory(name: 'Infrastructure', messageRegex: RegExp('timeout')),
],
),
);
// ...
}The most useful options:
linkUrlTemplates/linkNameTemplates— templates keyed by link type, applied to links whose URL is not absolute.{}(or%s) is replaced by the link value, soallure.issue('AUTH-123')resolves to the full tracker URL.executorInfo— CI or launcher metadata written toexecutor.json(name, type, build name, build URL, report URL, build order).environmentInfo— key-value pairs written toenvironment.properties, merged with the config file values.categories— custom defect category definitions written tocategories.json, matching results by message or trace regular expressions.globalLabels— labels added to every test result, merged with the config file values.listeners— a list ofAllureLifecycleListenerimplementations. A listener observes lifecycle events (test start/stop/write, step stop, container write, attachments, global errors) and can mutate a result before it is written — for example, to add labels or scrub data. A throwing listener is reported to stderr without failing the run.
WARNING
installAllure() is safe to call more than once in the same process as long as lifecycle (and frameworkLabelResolver) are omitted or identical each time — a bare installAllure() never disturbs an already-installed configuration. Calling it again with a genuinely different lifecycle instance throws a StateError instead of silently keeping the first one.
Building a custom integration with allure_dart_commons
Use allure_dart_commons when you need low-level control over the lifecycle:
import 'package:allure_dart_commons/allure_dart_commons.dart';
Future<void> main() async {
final lifecycle = AllureLifecycle(
writer: AllureResultsWriter(outputDirectory: 'allure-results'),
);
final testUuid = lifecycle.startTest(
name: 'login works',
fullName: 'auth/login_test.dart#login works',
);
lifecycle.startStep(testUuid, 'submit credentials');
lifecycle.stopStep(testUuid, status: AllureStatus.passed);
await lifecycle.stopTest(testUuid, status: AllureStatus.passed);
await lifecycle.writeTest(testUuid);
}The main low-level types are:
AllureLifecycle— starts, updates, stops, and writes tests, fixtures, and steps; handles attachments and run-level dataAllureResultsWriter— writes result files, containers, and attachments to the results directoryAllureConfig— loadsallure-dart.yamlAllureLifecycleListener— lifecycle event hooksAllureStatus,AllureStatusDetails,AllureLabel,AllureLink,AllureParameter, and the other model types
For streaming and late artifacts, the lifecycle exposes addAttachmentStreamToRoot and addPreparedAttachmentToRoot, which write the payload durably before the result references it. To route the top-level runtime API to your own lifecycle, wire a MessageTestRuntime with setGlobalTestRuntime and carry the execution context across async boundaries with runWithAllureContext.