Skip to content

Testing with the fake server

package:crm_sdk/testing.dart has a fake PurrCRM for your tests and demo builds. It is pure Dart and reads no files.

  • FakeCrmServer answers every route the package uses, in memory, the way PurrCRM does: parts switched off answer 404, a conversation that is not the user’s is not found, ratings are given once, surveys check their answers, events are kept once. Each new user finds three conversations (one with an unread reply), a ticket in progress, a help center of five articles, and one in-app message of each kind, under the product name you give it.
  • FakeCrmClient(server) is an http.Client over it: hand it to CrmConfig.httpClient, with apiBase: FakeCrmClient.base. Set offline to fail every request, and latency to slow them; requests keeps what was asked.
  • StandaloneCrmHost is the server’s world when you have no fake platform of your own: any bearer token is a user, every part is on, and what PurrCRM would push is kept in notices.
  • What your team does: server.staffReply(conversationId, text), server.closeConversation(id), server.sendTest(MessageKind.banner); and helpBot, staffReplyAfter and messengerWaitFor to change how it answers. conversationsOf(userId), articleRatings, events and exportOf(userId) say what happened.
testWidgets('the badge counts the reply waiting', (tester) async {
final server = FakeCrmServer(productName: 'Northwind')..staffReplyAfter = null;
final crm = Crm(
config: CrmConfig(
apiBase: FakeCrmClient.base,
appKey: server.appKey,
appVersion: '1.0.0',
productName: 'Northwind',
httpClient: FakeCrmClient(server),
followLifecycle: false,
flushEvery: Duration.zero,
store: MemoryCrmStore(prefix: 'test'),
),
// OneUser: a CrmTokens of the test's that answers 'user-1'.
auth: const CrmAuth.tokens(OneUser('user-1')),
)
..session(const CrmSession(guestId: 'user-1'))
..setReady(true);
// The app's public face (its parts) is read as the facade starts.
await tester.runAsync(crm.start);
await tester.pumpWidget(CrmScope(crm: crm, child: const MyApp()));
await tester.pump(const Duration(milliseconds: 100));
expect(find.text('1'), findsOneWidget);
// Let go of everything inside the test body: a facade disposed in a
// tearDown runs outside the test's fake clock, and hangs.
await tester.pumpWidget(const SizedBox.shrink());
server.close();
await crm.dispose();
await tester.pump(const Duration(seconds: 1));
});

Make the facade inside the test body too, for the same reason. A page transition takes most of a second in a widget test: pump through it before looking.

If your tests already fake your own backend, make the server over it with your own FakeCrmHost (the clock, which parts are on, who a token is, whether a user turned events off, and what becomes of a notice), and hand it each request your fake receives for PurrCRM (handle, or hold then answer).

Run your app over the fake for a demo or a screenshot build: hand FakeCrmClient(FakeCrmServer()) to CrmConfig.httpClient, and any name signs in. The package’s example/ app does this by default:

Terminal window
cd example
flutter run # over the fake
flutter run --dart-define=CRM_API_BASE=https://app.purrcrm.com \
--dart-define=CRM_APP_KEY=app_... # against your PurrCRM

See the testing reference for every class.