Skip to main content

Testing

Testing a client application can be a crucial part of ensuring its functionality and performance. When it comes to mobile/web applications, spinning up a full server to test against may not always be the best option. In the following sections, we will go through a couple of alternatives.

Widget Testing

For widgets that interface with Connect, it will generally be preferable to mock your RPCs since a backend may not be available or desirable to access in a unit test. The easiest way to do this is via Connect's FakeTransportBuilder

Mocking Transports

FakeTransportBuilder from package:connectrpc/test.dart creates an in-memory server with your own RPC implementations. It allows you to mock a backend to cover different behaviors in your widget.

To illustrate, let's set up a very simple ELIZA service:

import 'package:connectrpc/connect.dart';
import 'package:connectrpc/test.dart';
import './gen/eliza.connect.spec.dart';

final fakeTransport = FakeTransportBuilder().unary(
ElizaService.say,
(_, __) async {
return SayResponse(sentence: 'I feel happy.');
},
).build();

It exposes four methods: unary, server, client, and bidi. All of them accept a Spec type. This can be obtained from the generated *.connect.spec.dart files.

Under the hood, this fake transport behaves like a server. This means that you can access request headers, raise errors with details, and also mock streaming responses. Here is an example that raises an error on the fourth request:

var sentences = <String>[];
final fakeTransport = FakeTransportBuilder().unary(
ElizaService.say,
(req, context) async {
sentences.add(req.sentence);
if (sentences.length > 3) {
throw ConnectException(
Code.resourceExhausted,
"I have no words anymore.",
);
}
context.responseHeaders.add("foo", "bar");
return SayResponse(sentence: 'I feel happy.');
},
).build();

You can also use expectations to assert that your client sends requests as expected:

final fakeTransport = FakeTransportBuilder().unary(
ElizaService.say,
(req, _) async {
expect(req.sentence, "how do you feel?");
return SayResponse(sentence: 'I feel happy.');
},
).build();

The FakeTransportBuilder can be chained to provide mocks for multiple methods:

final fakeTransport = FakeTransportBuilder().unary(
ElizaService.say,
(req, _) async {
return SayResponse(sentence: 'I feel happy.');
},
).server(ElizaService.converse, (req, _) async* {
yield ConverseResponse();
}).build();