NAME
Test2::Thunderhorse - Test2-native testing tools for Thunderhorse applications
SYNOPSIS
use v5.40;
use Test2::V1;
use Test2::Thunderhorse;
use HTTP::Request::Common;
package MyApp {
use Mooish::Base -standard;
extends 'Thunderhorse::App';
sub build ($self) {
$self->router->add(
'/hello' => {
to => sub ($self, $ctx) {
return 'Hello World';
}
}
);
}
}
my $app = MyApp->new;
# Test HTTP endpoints
http $app, GET '/hello';
http_status_is 200;
http_header_is 'Content-Type', 'text/html; charset=utf-8';
http_text_is 'Hello World';
# Test WebSocket connections
websocket $app, '/ws/echo';
websocket->send_text('test');
is websocket->receive_text, 'echo: test';
websocket->close;
# Test Server-Sent Events
sse $app, '/events';
is sse->receive_event->{data}, 'message';
sse->close;
done_testing;
DESCRIPTION
Test2::Thunderhorse provides a Test2-native interface for testing Thunderhorse applications. It wraps PAGI::Test::Client and provides convenient exported functions for testing HTTP requests, WebSocket connections, and Server-Sent Events streams.
The module automatically sets PAGI_ENV to test and prevents loading in non-test environments. All exported functions integrate with Test2's context system for proper test result reporting.
EXPORTED FUNCTIONS
pagi_run
my $state = pagi_run $app, $coderef;
Fires a PAGI startup lifecycle event, then executes the $coderef. This triggers on_startup and on_shutdown hooks in Thunderhorse, and may be mandatory depending on the app configuration. Inside $coderef, test the app normally using other functions.
Returns the shared state from lifespan.
http
http $app, GET '/path';
http $app, POST '/path', Content => 'data';
my $response = http;
Makes an HTTP request to the application and stores the response as the current HTTP response. When called without arguments, returns the last HTTP response object.
The first argument is the Thunderhorse application object. The second argument is an HTTP::Request object, typically created using HTTP::Request::Common functions like GET, POST, PUT, DELETE, etc.
The returned response object is a PAGI::Test::Response object with methods like status, text, json, header, etc.
http_status_is
http_status_is 200;
http_status_is 404;
Tests that the last HTTP response status code matches the expected value. This is a Test2 assertion that will pass or fail appropriately.
This helper is here to test a common case of comparing the HTTP status code. It works the same as is http->status, $status, 'status ok'. Use http->status with other Test2 tools to do more complex comparisons.
http_header_is
http_header_is 'Content-Type', 'text/html; charset=utf-8';
http_header_is 'X-Custom-Header', 'value';
Tests that a specific header in the last HTTP response matches the expected value. This is a Test2 assertion.
This helper is here to test a common case of comparing the HTTP header's value. It works the same as is http->header($header), $value, "$header header ok". Use http->header with other Test2 tools to do more complex comparisons.
http_text_is
http_text_is 'Hello World';
http_text_is '{"status":"ok"}';
Tests that the body of the last HTTP response matches the expected value. This is a Test2 assertion.
This helper is here to test a common case of comparing the HTTP body. It works the same as is http->text, $body, 'body ok'. Use http->text with other Test2 tools to do more complex comparisons.
websocket
websocket $app, '/ws/path';
websocket $app, '/ws/path', headers => {...};
my $ws = websocket;
Opens a WebSocket connection to the application and stores it as the current WebSocket connection. When called without arguments, returns the last WebSocket connection object.
The first argument is the Thunderhorse application object. The second argument is the WebSocket endpoint path. Additional arguments are passed as options to the underlying client.
If the WebSocket connection fails to establish, a test failure is recorded.
The returned WebSocket object is a PAGI::Test::WebSocket object with methods like:
send_text($text)- Send text messagesend_json($data)- Send JSON messagereceive_text- Receive text messagereceive_json- Receive and decode JSON messageclose- Close the connectionis_closed- Check if connection is closed
sse
sse $app, '/events/path';
sse $app, '/events/path', headers => {...};
my $sse = sse;
Opens a Server-Sent Events connection to the application and stores it as the current SSE connection. When called without arguments, returns the last SSE connection object.
The first argument is the Thunderhorse application object. The second argument is the SSE endpoint path. Additional arguments are passed as options to the underlying client.
If the SSE connection fails to establish, a test failure is recorded.
The returned SSE object is a PAGI::Test::SSE object with methods like:
receive_event- Receive next event as hashref with keys:data,event,idreceive_json- Receive and decode JSON from next event's dataclose- Close the connectionis_closed- Check if connection is closed
TESTING PATTERNS
Basic HTTP Testing
subtest 'should handle GET request' => sub {
http $app, GET '/api/users';
http_status_is 200;
http_header_is 'Content-Type', 'application/json; charset=utf-8';
my $data = http->json;
is scalar($data->@*), 3, 'got 3 users';
};
Testing with POST Data
subtest 'should create user' => sub {
http $app, POST '/api/users',
Content_Type => 'application/json',
Content => '{"name":"John"}';
http_status_is 201;
http_text_is '{"status":"created"}';
};
WebSocket Testing
subtest 'should echo messages' => sub {
websocket $app, '/ws/echo';
websocket->send_text('hello');
is websocket->receive_text, 'echo: hello';
websocket->send_json({msg => 'test'});
is websocket->receive_json, {msg => 'test', echoed => 1};
websocket->close;
ok websocket->is_closed;
};
SSE Testing
subtest 'should stream events' => sub {
sse $app, '/events/stream';
my $event = sse->receive_event;
is $event->{data}, 'first message';
is $event->{event}, 'update';
is $event->{id}, 1;
my $json = sse->receive_json;
is $json->{count}, 42;
sse->close;
};
ENVIRONMENT
The module automatically sets PAGI_ENV to test when loaded and will die if loaded in an environment where PAGI_ENV is already set to something other than test. This ensures tests always run in test mode.
SEE ALSO
Test2::V1, PAGI::Test::Client, Thunderhorse, HTTP::Request::Common
AUTHOR
Bartosz Jarzyna <bbrtj.pro@gmail.com>
COPYRIGHT AND LICENSE
Copyright (C) 2025 by Bartosz Jarzyna
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.