data_builder_test 0.11.0
data_builder_test: ^0.11.0 copied to clipboard
craft_runner builder plus annotations that generate immutable fluent data builders from @dataBuilder-annotated test classes.
data_builder_test #
A craft_runner builder that turns a
plain schema class into an immutable fluent builder, for constructing test
fixtures without a wall of map literals.
final body = const OrderBuilder()
.withId('ord_1')
.withTotal(4200)
.build(); // => {'id': 'ord_1', 'total': 4200, 'status': 'pending'}
Every field you don't touch falls back to the default declared on the schema, so a test states only what it actually cares about.
Setup #
dev_dependencies:
data_builder_test: ^0.11.0
Declare the builder in craft_runner.yaml:
roots: [test]
exclude: ['.data_builder.dart']
builders:
data_builder_test:DataBuilderCraftBuilder:
Then run craft_runner craft, or craft_runner watch to regenerate on save.
Writing a schema #
Annotate a class with @dataBuilder, have it extend its own generated builder,
and declare a part:
import 'package:data_builder_test/annotations.dart';
part 'order.data_builder.dart';
@dataBuilder
class Order extends OrderBuilder {
String id = 'ord_1';
int total = 0;
String status = 'pending';
@DataField(key: 'created_at')
String? createdAt;
}
craft_runner writes order.data_builder.dart next to it containing
OrderBuilder extends Buildable — a const default constructor, a withX(...)
per field, and build(). All storage is private, and every withX returns a
new instance.
@DataField #
| option | effect |
|---|---|
key |
map key to serialize under (defaults to the field name) |
includeIfNull |
when false, the entry is dropped from build() if null |
settable |
when false, no withX is generated and the field always serializes its default |
Defaults #
Defaults are not seeded through the constructor. Each field carries a private
_<field>IsSet flag and build() emits isSet ? value : default, so an
untouched field falls back to its declared initializer (or null). This keeps
the const constructor trivial and avoids non-const collection literals in a
const context.
Nested builders #
A field whose type isn't a known scalar is treated as a nested builder: its
storage type becomes <Type>Builder and build() serializes it recursively —
value.build() for a single field, value.map((e) => e.build()).toList() for a
List<...>.
@dataBuilder
class Cart extends CartBuilder {
List<Order> orders = [];
}
const CartBuilder().withOrders([const OrderBuilder().withId('ord_2')]).build();
Named presets #
Use @DataPreset() when several fields represent one consistent state. A
zero-argument void method describes the state using direct field assignments;
the generator exposes it as an immutable withX() operation.
@dataBuilder
class VerificationStatus extends VerificationStatusBuilder {
String key = 'InProgress';
String label = 'In progress';
@DataPreset()
void pending() {
key = 'PendingEntry';
label = 'Required';
}
@DataPreset()
void approved() {
key = 'Approved';
label = 'Approved';
}
}
final status = const VerificationStatusBuilder()
.withPending()
.withLabel('Waiting for documents')
.build();
// {'key': 'PendingEntry', 'label': 'Waiting for documents'}
Preset bodies must be synchronous, accept no parameters, return void, and
contain only simple assignments to settable fields on the same schema. Calls
obey normal fluent ordering, so a later setter or preset wins.
Notes #
- Parsing is syntactic only, inherited from craft_runner — the schema's fields must be declared on the class itself, not inherited from elsewhere.
- Generated files end in
.data_builder.dart; add that toexcludeso the builder doesn't read its own output.
License #
MIT