Skip to content

Testing

Arun Prakash edited this page Oct 25, 2026 · 3 revisions

Testing

package:wordpress_client/testing.dart provides a Dio adapter that serves canned responses, so code that uses the client can be tested without a live site or real network.

import 'package:dio/dio.dart';
import 'package:wordpress_client/testing.dart';
import 'package:wordpress_client/wordpress_client.dart';

Build a client on the mock

final adapter = MockWordpressAdapter();

final client = WordpressClient.fromDioInstance(
  baseUrl: Uri.parse('https://example.com/wp-json/wp/v2'),
  instance: Dio()..httpClientAdapter = adapter,
  bootstrapper: (b) => b.build(),                // add auth, retries, cache, ... as in production
);

Script responses

Routes match on HTTP method and path (a String is matched as a path suffix; pass a RegExp for patterns). The newest route wins.

adapter
  ..reply('GET', '/posts', MockResponse.paged(
    [{'id': 1, 'title': {'rendered': 'Hello'}}],
    total: 1,
    totalPages: 1,
  ))
  ..reply('POST', '/posts', MockResponse.wpError(
    statusCode: 403,
    code: 'rest_cannot_create',
    message: 'Sorry, you are not allowed to create posts.',
  ))
  ..reply('GET', '/pages', const MockResponse(statusCode: 200, body: []));
MockResponse Result
MockResponse(statusCode:, body:, headers:, delay:) Any response. body can be a map, list or string
MockResponse.paged(items, total:, totalPages:) List with X-WP-Total and X-WP-TotalPages
MockResponse.wpError(statusCode:, code:, message:) A WordPress error body
MockResponse.connectionError() A transport failure (NetworkFailure)
MockResponse.timeout() A timeout (NetworkFailure(kind: timeout))

Dynamic and sequential answers:

adapter.on('GET', RegExp(r'/posts/\d+$'), (req) {
  final id = int.parse(req.path.split('/').last);
  return MockResponse(body: {'id': id, 'title': {'rendered': 'Post $id'}});
});

adapter.replySequence('GET', '/pages', [
  MockResponse.timeout(),                       // first call fails
  const MockResponse(body: []),                 // then succeeds (the last entry repeats)
]);

MockWordpressAdapter(strict: true) throws a StateError for a request without a route; the default answers 404 rest_no_route.

Assert on what was sent

await client.posts.create(CreatePostRequest(title: 'Hi'));

expect(adapter.callCount(method: 'POST', path: '/posts'), 1);
expect(adapter.lastRequest!.body.toString(), contains('Hi'));
expect(adapter.lastRequest!.headers['Authorization'], isNotNull);   // when a default authorization is set
expect(adapter.requests.map((r) => r.uri.path), contains('/wp-json/wp/v2/posts'));
adapter.clearRequests();

RecordedRequest has method, uri, path, headers, body (its type depends on the request: a map, a JSON string, form data).

Test retries without waiting

final client = WordpressClient.fromDioInstance(
  baseUrl: baseUrl,
  instance: Dio()..httpClientAdapter = adapter,
  bootstrapper: (b) => b
      .withRetryPolicy(RetryPolicy(maxAttempts: 3, sleep: (_) async {}))   // no real delay
      .build(),
);

adapter.replySequence('GET', '/posts', [
  MockResponse.wpError(statusCode: 503, code: 'unavailable'),
  MockResponse.paged([], total: 0, totalPages: 0),
]);

final r = await client.posts.list(ListPostRequest());
expect(r.isSuccessful, isTrue);
expect(r.extra?[WordpressRequest.metaAttempts], 2);

Test your own code

Inject the client (or a factory) into your repositories and pass this one in tests. Because the mock goes through the real pipeline, middlewares, caching, auth headers and typed failures behave as in production.

Clone this wiki locally