メール本体を組み立てるロジックとメール送信部分が結合しているようなシステムを自動テストする際に、 一時的に立ち上がるメールサーバが欲しくなります。 また、メールサーバにどんなメールが届くのかをチェックできると嬉しいですね。
Test::TCPとNet::Server::Mail::SMTPを使えば、これを実現できます。
Test::TCPは、開いているポートをスキャンして、サーバ側のコードとクライアント側のコードを実行してくれるモジュールです。 Net::Server::Mail::SMTPは、メールサーバを簡単に書けるようにしてくれるモジュールです。各SMTPコマンドに対して処理を書くことができます。
この2つのモジュールを組み合わせれば、以下のようにテストを書くことができます。
use strict; use warnings; use Test::More; use Test::TCP; use Net::SMTP; use Net::Server::Mail::SMTP; use Email::MIME; use Email::Address::Loose; use Email::MIME::MobileJP::Parser; my $from = 'test-from@example.com'; my $to = 'test-to@example.com'; my $body = 'test-body'; my $mime = Email::MIME->create( header => [ From => $from, To => $to, Subject => 'test-subject' ], attributes => { content_type => 'text/plain', charset => 'ISO-2022-JP', encoding => '7bit', }, body => $body ); test_tcp( client => sub { my $port = shift; eval { my $smtp = Net::SMTP->new( Host => 'localhost', Port => $port, Hello => '[localhost]' ); $smtp->mail('test-from@example.com'); $smtp->to('test-to@example.com'); $smtp->data(); $smtp->datasend($mime->as_string); $smtp->quit; }; if ($@) { warn $@; } }, server => sub { my $port = shift; my $sock = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'tcp', Listen => 1, ) or die "Cannot open server socket: $!"; # チェック用のリクエストが来るのでパスする $sock->accept(); while (my $remote = $sock->accept()) { eval { my $smtp = Net::Server::Mail::SMTP->new('socket' => $remote); $smtp->set_callback( 'RCPT' => sub { my $sess = shift; my $rcpt = shift; my ($email) = Email::Address::Loose->parse($rcpt); my $domain = $email->host; return (0, 513, 'Syntax error.') unless $domain; return 1; } ); $smtp->set_callback( 'DATA' => sub { my $sess = shift; my $data = shift; my $mail = Email::MIME::MobileJP::Parser->new($data); my $from = $mail->from(); my $body = $mail->mail->body; is $from->address, 'test-from@example.com'; like $body, qr/test-body/; return (1, 250, 'message queued'); } ); $smtp->process(); }; if ($@) { warn $@; $remote->close(); } } } ); done_testing;
Test::TCPは、serverに記述されたメールサーバが起動したあとに、clientに記述されたプログラムを実行してくれるようになっています。 便利ですね。