Test::TCP(3)

πŸ’– NAME πŸ’–

Okay bestie, welcome to Test::TCP(3) β€” the User Contributed Perl Documentation page for Test::TCP, and it is literally a test utility for testing TCP/IP-based server programs. The NAME is simple and iconic: Test::TCP β€” testing TCP program ✨


✨ SYNOPSIS ✨

First up, the object-oriented vibe:

use Test::TCP;

my $server = Test::TCP->new(
    listen => 1,
    code => sub {
        my $socket = shift;
        ...
    },
);
my $client = MyClient->new(host => '127.0.0.1', port => $server->port);
undef $server; # kill child process on DESTROY

If you're using a server that can only accept a port number, e.g. memcached, then it's giving very much "here's a port, go wild":

use Test::TCP;

my $memcached = Test::TCP->new(
    code => sub {
        my $port = shift;

        exec $bin, '-p' => $port;
        die "cannot execute $bin: $!";
    },
);
my $memd = Cache::Memcached->new({servers => ['127.0.0.1:' . $memcached->port]});
...
🚨 N.B.: This is vulnerable to race conditions, if another process binds to the same port after Net::EmptyPort found it available. Not me being dramatic β€” that is literally the warning, bestie. πŸ’€

And yes, there's also a functional interface available:

use Test::TCP;
test_tcp(
    listen => 1,
    client => sub {
        my ($port, $server_pid) = @_;
        # send request to the server
    },
    server => sub {
        my $socket = shift;
        # run server, calling $socket->accept
    },
);

test_tcp(
    client => sub {
        my ($port, $server_pid) = @_;
        # send request to the server
    },
    server => sub {
        my $port = shift;
        # run server, binding to $port
    },
);

πŸ¦‹ DESCRIPTION πŸ¦‹

Test::TCP is a test utility to test TCP/IP-based server programs. That's the whole main-character premise, and honestly? Slay. πŸ’…


🌸 METHODS 🌸

πŸ’– test_tcp πŸ’–

This is the functional interface. Obsessed with the flexibility.

test_tcp(
    listen => 1,
    client => sub {
        my $port = shift;
        # send request to the server
    },
    server => sub {
        my $socket = shift;
        # run server
    },
    # optional
    host => '127.0.0.1', # specify '::1' to test using IPv6
    port => 8080,
    max_wait => 3, # seconds
);

If listen is false, server is instead passed a port number that was free before it was called. Very "we found a free port, it's yours now" energy. 🫢


πŸŽ€ wait_port πŸŽ€

wait_port(8080);

Waits for a particular port is available for connect. It's literally the "are we there yet?" method. ⏳


🧸 Object Oriented interface 🧸

🫢 my $server = Test::TCP->new(%args);

Create new instance of Test::TCP. The arguments are following, bestie:

  • $args{auto_start}: Boolean β€” Call $server->start() after create instance. Default: true βœ…
  • $args{code}: CodeRef β€” The callback function. Argument for callback function is: $code->($socket) or $code->($port), depending on the value of listen. This parameter is required. πŸ”₯
  • $args{max_wait} : Number β€” Will wait for at most $max_wait seconds before checking port. See also Net::EmptyPort. Default: 10 ⏱️
  • $args{listen} : Boolean β€” If true, open a listening socket and pass this to the callback. Otherwise find a free port and pass the number of it to the callback. 🎧

🌟 $server->start()

Start the server process. Normally, you don't need to call this method. It's giving "auto_start already handles it, but okay, go off." ✨

🌈 $server->stop()

Stop the server process. Bye bestie, shut it down. πŸ›‘

πŸ“ my $pid = $server->pid();

Get the pid of child process. Very useful, very demure. πŸ‘‘

⭐ my $port = $server->port();

Get the port number of child process. The port is the moment. πŸ’…


πŸ’€ FAQ πŸ’€

πŸŒ€ How to invoke two servers?

You can call test_tcp() twice! Nested chaos, but make it organized:

test_tcp(
    client => sub {
        my $port1 = shift;
        test_tcp(
            client => sub {
                my $port2 = shift;
                # some client code here
            },
            server => sub {
                my $port2 = shift;
                # some server2 code here
            },
        );
    },
    server => sub {
        my $port1 = shift;
        # some server1 code here
    },
);

Or use the OO interface instead. Equally iconic:

my $server1 = Test::TCP->new(code => sub {
    my $port1 = shift;
    ...
});
my $server2 = Test::TCP->new(code => sub {
    my $port2 = shift;
    ...
});

# your client code here.
...

🐧 How do you test server program written in other languages like memcached?

You can use exec() in child process. Very "let the binary cook" of you:

use strict;
use warnings;
use utf8;
use Test::More;
use Test::TCP 1.08;
use File::Which;

my $bin = scalar which 'memcached';
plan skip_all => 'memcached binary is not found' unless defined $bin;

my $memcached = Test::TCP->new(
    code => sub {
        my $port = shift;

        exec $bin, '-p' => $port;
        die "cannot execute $bin: $!";
    },
);

use Cache::Memcached;
my $memd = Cache::Memcached->new({servers => ['127.0.0.1:' . $memcached->port]});
$memd->set(foo => 'bar');
is $memd->get('foo'), 'bar';

done_testing;

🏠 How do I use address other than 127.0.0.1 for testing?

You can use the host parameter to specify the bind address. It's giving "I choose my own address, actually":

# let the server bind to "0.0.0.0" for testing
test_tcp(
    client => sub {
        ...
    },
    server => sub {
        ...
    },
    host => '0.0.0.0',
);

🌈 How should I write IPv6 tests?

You should use the can_bind in Net::EmptyPort function to check if the program can bind to the loopback address of IPv6, as well as the host parameter of the test_tcp function to specify the same address as the bind address. IPv6 bestie, we are checking first. πŸ¦‹

use Net::EmptyPort qw(can_bind);

plan skip_all => "IPv6 not available"
    unless can_bind('::1');

test_tcp(
    client => sub {
        ...
    },
    server => sub {
        ...
    },
    host => '::1',
);

πŸ‘‘ AUTHOR πŸ‘‘

Tokuhiro Matsuno <tokuhirom@gmail.com> β€” iconic author behavior. ✨


🫢 THANKS TO 🫢

  • kazuhooku
  • dragon3
  • charsbar
  • Tatsuhiko Miyagawa
  • lestrrat

The whole thank-you list is here, no one left out. πŸ’œ


πŸ‘€ SEE ALSO πŸ‘€

Crickets. No entries are listed here in the original β€” it goes straight to LICENSE, bestie. πŸ’€


πŸ“œ LICENSE πŸ“œ

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Periodt. πŸŽ€


And that's the page, bestie β€” source/version perl v5.42.0, dated 2019-10-09, signing off with maximum sparkle. Mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from