Jeen - Yet anothere techlog

STFUAWSC

Test::More - Use ‘Subtest’ Instead of {} Block

여태껏 테스트코드를 써나가면서 그냥 {} 블록안에 일정한 흐름의 테스트 코드를 적곤 했었습니다.

~~~ perl use Test::More; use HTTP::Request::Common; use Catalyst::Test ‘MyApp::Web’;

# Access / {

my $res = request('/');
ok($res->is_success, "Request should be succeed");
ok($res->header('.......'), 'Header xxxxx exist');
like($res->content, qr|some sentences|, "Found some sentences")

}

# Login {

my $req = POST('/login', \[ 'username' => 'blahblah', 'password' => 'blahblah' \]);
my $res = request($req);
ok($res->is_success, "Request should be suceed");
....

}

# some action {

...;

} ~~~

일단 뭐 {} 블록으로 각 action 에 대한 접근결과를 테스트하고, # 로 주석표시를 하면서 하나둘 하나둘 각 블록에 $req, $res 를 추가했었습니다. 일단 뭐 {} 블록으로 묶으면서 $req, $res 따위의 변수의 재정의 등에 관한 경고같은 것이 안나와서 좋고, 제대로 분류가 되는 느낌이기도 했습니다.

그냥 subtest 를 사용하면 어떨까?

그러는 도중에 Test::More 매뉴얼을 읽어보다가 subtest 를 사용할 수 있다는 것을 알게 되었습니다.

~~~ perl use HTTP::Request::Common; use Catalyst::Test ‘MyApp::Web’; use Test::More;

subtest ‘access to /’ => sub {

my $res = request('/');
ok($res->is_success, "Request should be succeed");
ok($res->header('.......'), 'Header xxxxx exist');
like($res->content, qr|some sentences|, "Found some sentences")

};

subtest ‘Login’ => sub {

my $req = POST('/login', \[ 'username' => 'blahblah', 'password' => 'blahblah' \]);
my $res = request($req);
ok($res->is_success, "Request should be suceed");
....

}; ~~~

subtest 를 사용하면서 코드는 위처럼 바뀌었습니다. # 로 주석처리를 할 필요도 없고, 해당 subtest 마다 명확하게 무엇을 하는 지 확실하게 의미있는 subtest 의 이름으로 정해줄 수 있었습니다.

subtest 로 테스트를 썼을 경우는 TAP 결과는 다음과 같습니다.

~~~ bash $ prove t/myapp.t ok 1 – access to /

ok 1 - Request should be succeed
ok 2 - Header xxxxx exist
ok 3 - Found some sentences
1..3

ok 2 – Login

ok 1 - Request should be suceed
...
1..3

…. 1..3 ok All tests successful. Files=1, Tests=3, 10 wallclock secs ( 0.07 usr 0.01 sys + 6.16 cusr 0.61 csys = 6.85 CPU) Result: PASS ~~~

전체 테스트 횟수는 subtest 안의 테스트 항목이 아니라, subtest 의 갯수가 되는 것입니다.

그리고 subtest 를 사용하지 않고 {} 블록을 이용해서 flat 하게 정의했을 때는 아래와 같습니다.

bash … ok 1 – Request should be succeed ok 2 – Header xxxxx exist ok 3 – Found some sentences ok 4 – Request should be suceed …

테스트 항목 1,4 번의 메시지가 같기 때문에 매번 달리 지정해줘야 되는 수고가 발생하죠. 그리고 각각의 테스트 항목 하나하나가 테스트 갯수가 되는 것입니다.

사실은 위의 Jenkins 상의 Test Result Trend 의 그래프가 어느 순간 갑자기 뚝 떨어지길래, 어라 이상하다 싶어서 살펴본 결과, subtest 기준으로 카운트되어있는 것을 확인했습니다.

Jenkins Test Result Trend 의 낙폭

대충 위의 #58 번 빌드가 그 쯤이 되겠네요.

아무튼 Jenkins 도입이후에 좀 더 테스트코드에 더 신경을 쓰고 있는 요즘입니다.

Perl - Mail Send II

  • Perl – Mail Send

    이전에 Perl 에서 Mail Send 관련해서 한번 깨작거린 적이 있습니다.

    오늘은 이에 대해서 추가적인 수정이 필요한 부분이 있어서, MyApp::Mail 을 조금 더 건드려 봤습니다.

~~~ perl package MyApp::Mail; use Moose; use Moose::Util::TypeConstraints; use namespace::autoclean; use Email::MIME; use Email::Sender::Simple ‘sendmail’; use Email::Sender::Transport::SMTP; use Encode; use MIME::Types ();

subtype ‘MailAddresses’ => as ‘Str’;

coerce ‘MailAddresses’,

from 'ArrayRef',
    via { join(", ", (map { Encode::encode( "MIME-Header", $_ ) } @{ $_ })) };
 from 'Str',
     via { Encode::encode( "MIME-Header", $_ ) };

has to => (

is      => 'ro',
isa     => 'MailAddresses',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,
coerce    => 1,

);

has from => (

is      => 'ro',
isa     => 'Str',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,

);

has subject => (

is      => 'ro',
isa     => 'Str',
default => sub {
    Encode::encode( "MIME-Header", $_\[0\] );
},
required => 1,

);

has body => (

is  => 'ro',
isa => 'Str',

);

has transport => (

is  => 'ro',
isa => 'HashRef'

);

has attachments => (

is => 'ro',
isa => 'ArrayRef\[Path::Class::File\]',
default => sub { \[\] },

);

sub _get_parts {

my $self = shift;

my @parts;
push @parts,
    Email::MIME->create(
        attributes => {
            content_type => 'text/plain',
            charset      => 'utf8',
        },
        body => Encode::encode_utf8($self->body),
    );
for my $attachment (@{ $self->attachments }) {
    confess "File Not Found : $attachment" unless -f $attachment;
    my ($mime_type, $encoding) = MIME::Types::by_suffix($attachment->basename);
    $mime_type ||= 'multipart/mixed';
    my $file_body = $attachment->slurp;
    push @parts, Email::MIME->create(
        attributes => {
            content_type => $mime_type,
            name         => $attachment->basename,
            filename     => $attachment->basename,
            encoding     => 'base64',
            disposition  => 'attachment',
        },
        body => $file_body,
    );
}
\@parts;

}

sub send {

my $self = shift;

my $opt = {};
if ($self->transport) {
    $opt->{transport} = Email::Sender::Transport::SMTP->new( $self->transport );
}

my $email = Email::MIME->create(
    header => \[
        From    => $self->from,
        To      => $self->to,
        Subject => $self->subject
    \],
    parts => $self->_get_parts,
);
sendmail($email, $opt);

}

PACKAGE–>meta->make_immutable;

1; ~~~

일단 수정을 필요로 한 부분은, 똑같은 메일 내용을 둘 이상에게 보내야 할 경우입니다. 일반적으로 업체에서 보내는 메일의 헤더를 보면 To 에 본인만 들어가 있을 겁니다. 이게 일반적이죠.

하지만 이번에는 사내 시스템 관련 리포트를 보내는 것이고, 특정 다수에게 보내며, 메일링이 없다는 전제하에서 움직였습니다.

변경된 부분은,

~~~ diff package MyApp::Mail; use Moose; +use Moose::Util::TypeConstraints; use namespace::autoclean; @@ -9, 13 +10, 22 @@ use Email::Sender::Transport::SMTP; use Encode; use MIME::Types ();

+subtype ‘MailAddresses’ => as ‘Str’;

+coerce ‘MailAddresses’, + from ‘ArrayRef’, + via { join(“, ”, (map { Encode::encode( “MIME-Header”, $ ) } @{ $ })) }; + from ‘Str’, + via { Encode::encode( “MIME-Header”, $_ ) }; + has to => (

is      => 'ro',
  • isa => ‘Str’,
  • isa => ‘MailAddresses’, default => sub { Encode::encode( “MIME-Header”, $_[0] ); }, required => 1,
  • coerce => 1, ); ~~~

입니다. subtype 을 지정하기 위해서 Moose::Util::TypeConstraints 를 use 할 필요가 있구요. To 헤더의 형식을 MailAddresses 라는 subtype 으로 받도록 합니다. 그냥 문자열 값이 들어오든 Array Reference 가 들어오든 그에 맞춰서 변환해주도록 했구요.

이전 글에서 간략하게 Mail 테스트 방법에 대해서 소개했는데, 현재 위처럼 수정한 다음에 아래와 같이 테스트코드를 작성해서 문제없이 작동하는 것을 확인할 수 있었습니다.

~~~ perl send-mail.t use strict; use warnings; use Test::Most; use_ok ‘MyApp::Mail’; use Path::Class::File;

BEGIN {

$ENV{EMAIL_SENDER_TRANSPORT} = 'Test';

};

my %data_set = (

to      => 'your@email.com',
from    => 'my@email.com',
subject => 'MailSender Test Subject',
body    => 'MailSender Test Body',

);

Send a mail w/ attachments

my $mailer = MyApp::Mail->new(

%data_set,
attachments => \[ 
    Path::Class::File->new('somefile.zip'),
    Path::Class::File->new('somefile.ee')
\],

);

$mailer->send;

Send a mail without attachments

my $mailer2 = MyApp::Mail->new(%data_set);

$mailer2->send;

Send a mail with Multiple Recipients

my $mailer3 = MyApp::Mail->new(

%data_set,
to => \[ 'your2@email.com', 'anotheremail@gmail.com' \],

);

$mailer3->send;

Just string

my $mailer4 = MyApp::Mail->new( %data_set, to => ‘mail-1@mail.com, mail-2@mail.com, mail-3@mail.com’, );

$mailer4->send;

my $deliveries = Email::Sender::Simple->default_transport->deliveries; for my $deliv (@{ $deliveries }) { my $sent_addresses = join “, ”, @{ $deliv->{successes} }; ok $deliv->{successes}, ‘Mail sent to ’. $sent_addresses; } done_testing(); ~~~

물론 $deliveries 변수를 덤프해보면 각각의 메일헤더들이 존재하고 좀 더 항목당 세밀하게 테스트를 수행할 수 있겠지만, 여기서는 단순하게 이정도로 그치도록 합니다.

사실은 뭐 To 안에 여러개의 메일이 들어간다는 전제로 ArrayRef 를 넣었지만, 사실 Str 을 받기에 To 에 대해서는 그냥 문자열로 mail-1@mail.com, mail-2@mail.com, mail-3@mail.com 으로 해도 가능합니다. 하지만 그냥 제 고집상 복수의 데이터셋이 들어갈 때는 왠지 ArrayRef 로 넣고 싶어졌을 뿐입니다. 존중받고 싶은 취향같은 것이죠.

[번역] PM’s Task Management @ EA

  • 프로젝트의 두목은 두명으로 나뉜다. 한명은 Creative Director, 다른 한명은 Project Manager. PM 은 예산/외주관리/리소스 관리의 모든 권한을 가지고, CD 가 그 이외의 권한을 가진다.

  • 권력의 관계는 CD>PM. CD 가 “좀 늦어도 되니까 이것을 한다” 라고 정하면 PM 은 그에 따라, 프로젝트를 짜맞춰야 한다. 단, CD 가 최종책임은 전부 덮어쓰는 것이 룰.

  • PM 이 실제로 스케쥴을 진행하는 방법은 리소스 관리를 하면서, 2두 단위로 마일스톤을 내놓는다. 마일스톤에는 반드시 목적이 있고, 상황에 맞춰서 최종 스케쥴은 항상 바뀐다.

  • 마일스톤 6개에 하나 정도는 빅 마일스톤, 프로젝트의 진행상황을 외부(상층부)에 대해 나타내는 [알파] 를 내놓는다.

  • 최종 스케쥴은 필요한 리소스를 전부 나열해서, 팀원 스스로 골라서 맡게 된다. 다음은 PM 이 팀의 실적을 보고 속도를 CD 와 상층부에 알리고, 어디가 병목이 되는 지 등을 관리한다.

  • 프로젝트 팀원은 항상 마일스톤에서 당면한 목표를 알고 있어서 정말로 프로세스를 가지고 생각할 수 있으며, PM 이 항상 스케쥴을 쌓고, 속도를 계산해서 무엇이 늦어지고 있는 지 알려줄 수 있기 때문에 편하다

  • 내용과 진행 관리를 완전히 나눠서 최종적으로 내용관리로 정리한다. 처음은 내용을 전부 리스트로 만든다. 어떻게든 모든 요소라고 생각되는 것을 가시화 해서 리스트로 만들고 “어느 정도의 작업이 있는가”를 계산해낸다.

  • 그리고 그 내용중에 “절대로 fun factor 에 빠질 수 없는 것” 등을 CD 가 가르쳐 준다. 그 다음은 마일스톤에서 쌓아가면서 스케쥴을 보고, 일정 실적을 쌓을 즘에 실제로 가능한 것인지 전부 리스트로 만든다.

  • 그래서 PM 에게 질문하면 “이 추가예산이 있다면 아웃소싱이 있다면 여기에 이것을 투입하면 이렇게 단축할 수 있다” 라는 매우 구체적인 스케쥴을 항상 가질 수 있다.

  • 덧붙여 CD>PM 이라는 권력관계가 되어 있는 것은 EA 의 게임 크리에이티브와 재미를 담보로 하는 것은 결국은 게임 디자이너가 아니면 안된다라는 생각을 가지고 있기 때문이다.

원문