DwRecordingServerTransport class

A DwServerTransport that answers reads from what the test prepared and keeps every write that left, for a test to assert on afterwards.

Hand it to DwCore instead of a client, and nothing has to stand up a Serverpod client to render a widget or watch it save:

final transport = DwRecordingServerTransport(
  classNames: {Task: 'Task'},
);

setUpAll(() {
  DwCore<ServerpodClientShared, Task>(
    config: const DwConfig(),
    transport: transport,
    dwAlerts: DwAlerts.init(logErrors: false, logFunction: (_) {}),
    getUserId: (_) => null,
  );
});

testWidgets('the button saves the task', (tester) async {
  await tester.pumpWidget(/* the feature */);
  await tester.tap(find.byIcon(Icons.check));
  await tester.pumpAndSettle();

  expect(transport.saves, hasLength(1));
  expect((transport.saves.single.model as Task).isDone, isTrue);
});

Reads must be prepared, writes need not be. A read nobody answered throws DwUnpreparedServerCall, because the test does not know what its subject fetches. A save answers by echoing the model back and a delete by succeeding, because there the assertion is about what left, and forcing a canned response for it would be ceremony with nothing behind it. Override either with answerSave / answerDelete when the response matters — an id assigned on insert, a rejected write.

One instance per core, and reset between tests: the core keeps the transport it was built with for its whole life, and DwCore can be built only once per process.

Implemented types

Constructors

DwRecordingServerTransport({Map<Type, String> classNames = const {}, SerializationManager? serializationManager})
An application passes serializationManager, and passes its own generated Protocol() — the same object its Serverpod client carries. It already names every model of the project correctly, and it is the only thing that can: a generated model is an abstract class with a private implementation, so its runtimeType is _NewsPostImpl and never the name the wire uses. It is also what a test needs to read a model back — a local snapshot rebuilds its response from stored JSON.

Properties

answerDelete Future<DwApiResponse<bool>> Function(DwRecordedDelete delete)?
Answers delete. Unset reports success.
getter/setter pair
answerGetAll Future<DwApiResponse<List<DwModelWrapper>>> Function(DwRecordedRead read)?
Answers getAll. Unset means every getAll throws.
getter/setter pair
answerGetCount Future<DwApiResponse<int>> Function(DwRecordedRead read)?
Answers getCount. Unset means every getCount throws.
getter/setter pair
answerGetOne Future<DwApiResponse<DwModelWrapper>> Function(DwRecordedRead read)?
Answers getOne. Unset means every getOne throws.
getter/setter pair
answerSave Future<DwApiResponse<DwModelWrapper>> Function(DwRecordedSave save)?
Answers saveModel. Unset echoes the saved model back, as a server that accepted it unchanged would.
getter/setter pair
answerSubscribe Stream<SerializableModel> Function(String channel)?
Answers subscribeOnUpdates. Unset opens a stream that never emits and never ends.
getter/setter pair
answerUploadDescription Future<DwUploadTicket?> Function(String? folder, String fileExtension, String? fileName)?
Answers getUploadDescription. Unset means it throws.
getter/setter pair
answerVerifyUpload Future<DwCloudFile?> Function(int fileId)?
Answers verifyUpload. Unset means it throws.
getter/setter pair
deletes List<DwRecordedDelete>
Every delete that left, oldest first.
final
hashCode int
The hash code for this object.
no setterinherited
reads List<DwRecordedRead>
Every read that left, oldest first — including the ones that then threw DwUnpreparedServerCall, so a failing test can be told what was asked.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
saves List<DwRecordedSave>
Every save that left, oldest first.
final
serializationManager SerializationManager
How models are named and rebuilt on the wire this transport speaks.
final

Methods

delete({required String className, required int modelId, String? apiGroup}) Future<DwApiResponse<bool>>
Deletes row modelId of className.
override
getAll({required String className, DwBackendFilter? filter, List<DwOrderBy>? orderByList, int? limit, int? offset, String? apiGroup}) Future<DwApiResponse<List<DwModelWrapper>>>
Reads the className rows matching filter, ordered and paginated.
override
getCount({required String className, DwBackendFilter? filter, String? apiGroup}) Future<DwApiResponse<int>>
Counts the className rows matching filter, server-side.
override
getOne({required String className, required DwBackendFilter filter, String? apiGroup}) Future<DwApiResponse<DwModelWrapper>>
Reads the single className row matching filter.
override
getUploadDescription({String? folder, required String fileExtension, String? fileName}) Future<DwUploadTicket?>
Asks the server to reserve a key and sign a one-shot upload for it.
override
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
reset() → void
Forgets everything recorded and every prepared answer.
saveModel({required DwModelWrapper wrappedModel, String? apiGroup}) Future<DwApiResponse<DwModelWrapper>>
Creates or updates wrappedModel; answers with the persisted model.
override
subscribeOnUpdates({required String channel}) Stream<SerializableModel>
Opens the server-side update stream for channel.
override
toString() String
A string representation of this object.
inherited
verifyUpload({required int fileId}) Future<DwCloudFile?>
Confirms with the server that the bytes for reservation fileId arrived, and answers with the stored file — null when the upload cannot be verified or the reservation is not the caller's.
override

Operators

operator ==(Object other) bool
The equality operator.
inherited