Skip to content

Testing

Lee Yunjin edited this page Oct 7, 2026 · 1 revision

Testing

Header: <cwist/sys/app/test_client.h>

cwist_test_client sends synthetic requests through an app's middleware and router in the same process, without opening a socket, so integration tests need no ports and have no startup or teardown races.

#include <cwist/app.h>
#include <cwist/sys/app/test_client.h>

int main(void) {
    cwist_app *app = cwist_app_create();
    register_routes(app);

    cwist_test_client *tc = cwist_test_client_create(app);

    cwist_http_response *res = cwist_test_client_post_json(tc, "/login", "{\"user\":\"ann\"}");
    CWIST_ASSERT_STATUS(res, 200);
    CWIST_ASSERT_HEADER(res, "Content-Type", "application/json");
    cwist_http_response_destroy(res);

    res = cwist_test_client_get(tc, "/me");          /* cookies from /login are sent */
    CWIST_ASSERT_BODY_CONTAINS(res, "ann");
    cwist_http_response_destroy(res);

    cwist_test_client_destroy(tc);
    cwist_app_destroy(app);
    return 0;
}

API

Function Description
cwist_test_client *cwist_test_client_create(cwist_app *app) / void cwist_test_client_destroy(cwist_test_client *c) Client bound to an app, with its own cookie jar.
cwist_test_client_get(c, path), _delete(c, path) Requests without a body.
cwist_test_client_post(c, path, body, content_type), _put, _patch Requests with a body.
cwist_test_client_post_json(c, path, json) POST with Content-Type: application/json.
cwist_test_client_request_ex(c, method, path, &opts) Full control over headers, cookies and query string (cwist_test_client_request_options).
cwist_test_client_set_cookie(c, name, value, path), _get_cookie(c, name), _clear_cookies(c) Cookie jar.

Each request function returns a response that you destroy with cwist_http_response_destroy().

Assertion macros print [ASSERT FAIL] with the file and line and call exit(1): CWIST_ASSERT_STATUS(res, status), CWIST_ASSERT_HEADER(res, name, value) and CWIST_ASSERT_BODY_CONTAINS(res, text).

Other ways to test

  • cwist_app_dispatch_memory() runs a raw HTTP request through the app and returns the serialized response (Application).
  • For socket-level tests, start the server with cwist_app_listen_ex(app, port, 1, 1) in one process, send requests, and stop it with cwist_shutdown_request(); call cwist_shutdown_reset() before starting another server in the same process.
  • cwist_mw_rate_limit_reset() clears rate-limiter state between tests.

Deferred (async) responses need a real connection; they cannot complete through in-memory dispatch.

The project's own tests

make test builds and runs every test under tests/; make SANITIZE=address,undefined test is what the ASan/UBSan CI job runs. CI also runs an h2spec conformance diff, interop tests, HTTPS performance gates, a raw-allocator gate (cwist audit --gate), durable-queue tests against real brokers, WASM builds, and Rust and Zig binding tests. Tutorial 29 shows the test client in a complete program.

Clone this wiki locally