这只是我在错误上运行的一个测试的示例。我想编辑它,以便我可以遍历错误列表。我会将这些错误放入哈希并创建一个for循环来迭代它们。我不确定它是如何完成的。 我将在下面显示测试和错误库。只需要一个小例子让我离开。
测试文件:
use lib('./t/lib/');
use Test::More tests => 3;
use ASC::Builder:Error;
#########################################################################################################
############## test for new() method in Error.pm - Test Case: HASH ####################################
#########################################################################################################
# error hash
my $error_hash = UNABLE_TO_PING_SWITCH_ERROR;
# error hash is passed into new and an error object is outputted
my $error_in = ASC::Builder::Error->new($error_hash);
# checks to see if the output object from new is an Error object
isa_ok($error_in, 'ASC::Builder::Error');
# checking that object can call the message() method
can_ok( $error_in, 'message');
# checks to see if the output message matches the message contained in the error hash(correct)
is($error_in->message(),( $error_hash->{message} ), 'Returns correct error message');
ErrorLibrary.pm
package ASC::Builder::ErrorLibrary;
use strict;
use warnings;
use parent 'Exporter';
# list of export error messages
our @EXPORT_OK = qw/
INCORRECT_CABLING_ERROR
UPDATE_IMAGE_ERROR
UNABLE_TO_PING_SWITCH_ERROR
/;
# error message list
use constant {
# wiki link included as a variable in this example
INCORRECT_CABLING_ERROR => {
code => "INCORRECT_CABLING_ERROR",
errorNum => 561,
category => 'Cabling Error',
message => "ToR cabling is not correct at T1.The uplinks must be cabled to exactly one t1 device group",
tt => { template => 'disabled'},
fatal => 1,
wiki_page =>'http://w.server-build.com/index.phpBuilder/ErrorCodes/INCORRECT_CABLING_ERROR',
},
UPDATE_IMAGE_ERROR => {
code => "UPDATE_IMAGE_ERROR",
errorNum => 556,
category => 'Switch Error',
message => "Cannot determine switch model",
tt => { template => 'disabled'},
fatal => 1,
wiki_page =>'http://www.server-build.com/index.php/NetMgmt/Builder/ErrorCodes/UPDATE_IMAGE_ERROR',
},
UNABLE_TO_PING_SWITCH_ERROR => {
code => "UNABLE_TO_PING_SWITCH_ERROR",
errorNum => 727,
category => 'Switch Error',
message => "Could not ping switch [% switch_ip %] in [% timeout %] seconds.",
tt => {template => 'disabled'},
fatal => 1,
wiki_page => 'http://www.server-build.com/index.php/Builder/ErrorCodes/UNABLE_TO_PING_SWITCH_ERROR',
},
UNKNOWN_CLIENT_CERT_ID_ERROR => {
code => "UNKNOWN_CLIENT_CERT_ID_ERROR",
errorNum => 681,
category => 'Services Error',
message => "Unknown client certificate id: [% cert_id %]",
tt => { template => 'disabled'},
fatal => 1,
wiki_page =>'http://www.server-build.com/index.php/Builder/ErrorCodes/UNKNOWN_CLIENT_CERT_ID_ERROR',
},
# add errors to this library
};
1;
我只是不确定如何创建我的列表。我应该创建一个包含输入,过程和输出的列表来进行测试。我有点困惑。
答案 0 :(得分:1)
经过长时间的聊天讨论,我建议采用以下方法进行单元测试,并对实际代码进行轻微重组,以使事情变得更容易。
我所做的更改
我重新构建了代码从模板创建错误消息的方式,以便不使用Template,因为从your previous question可以清楚地看出它有点矫枉过正。
它现在使用sprintf
和Timeout after %s seconds
之类的简单模式。我故意在我的示例中使用%s
,因为从来没有任何类型检查,但它当然可以添加。此消息的参数作为从第二个参数开始的键/值对列表传递给构造函数。
my $e = Error->new(CONSTANT, foo => 'bar');
示例ErrorLibrary
第一个参数CONSTANT
仍来自您的错误库。我已经包含了以下简化示例。
package ErrorList;
use strict;
use warnings;
use parent 'Exporter';
use constant {
ERROR_WIFI_CABLE_TOO_SHORT => {
category => 'Layer 1',
template => 'A WiFi cable of %s meters is too short.',
context => [qw(length)],
fatal => 1,
wiki_page => 'http://example.org',
},
ERROR_CABLE_HAS_WRONG_COLOR => {
category => 'Layer 1',
template => 'You cannot connect to %s using a %s cable.',
context => [qw(router color)],
fatal => 1,
wiki_page => 'http://example.org',
},
ERROR_I_AM_A_TEAPOT => {
category => 'Layer 3',
template => 'The device at %s is a teapot.',
context => [qw(ip)],
fatal => 0,
wiki_page => 'http://example.org',
},
};
our @EXPORT = qw(
ERROR_WIFI_CABLE_TOO_SHORT
ERROR_CABLE_HAS_WRONG_COLOR
ERROR_I_AM_A_TEAPOT
);
our @EXPORT_OK = qw(ERROR_WIFI_CABLE_TOO_SHORT);
context 是一个数组引用,其中包含构造时预期的键列表。
重构(简化)错误类
这个课程包括POD来解释它的功能。重要的方法是构造函数message
和stringify
。
package Error;
use strict;
use warnings;
=head1 NAME
Error - A handy error class
=head1 SYNOPSIS
use Error;
use ErrorList 'ERROR_WIFI_CABLE_TOO_SHORT';
my $e = Error->new(
ERROR_WIFI_CABLE_TOO_SHORT,
timeout => 30,
switch_ip => '127.0.0.1'
);
die $e->stringify;
=head1 DESCRIPTION
This class can create objects from a template and stringify them into a
log-compatible pattern. It makes sense to use it together
with L<ErrorList>.
=head1 METHODS
=head2 new($error, %args)
The constructor takes the error definition and a list of key/value pairs
with context information as its arguments.
...
=cut
sub new {
my ( $class, $error, %args ) = @_;
# initialize with the error data
my $self = $error;
# check required arguments...
foreach my $key ( @{ $self->{context} } ) {
die "$key is required" unless exists $args{$key};
# ... and take the ones we need
$self->{args}->{$key} = $args{$key}; # this could have a setter
}
return bless $self, $class;
}
=head2 category
This is the accessor for the category.
=cut
sub category {
return $_[0]->{category};
}
=head2 template
This is the accessor for the template.
=cut
sub template {
return $_[0]->{template};
}
=head2 fatal
This is the accessor for whether the error is fatal.
=cut
sub is_fatal {
return $_[0]->{fatal};
}
=head2 wiki_page
This is the accessor for the wiki_page.
=cut
sub wiki_page {
return $_[0]->{wiki_page};
}
=head2 context
This is the accessor for the context. The context is an array ref
of hash key names that are required as context arguments at construction.
=cut
sub context {
return $_[0]->{context};
}
=head2 category
This is the accessor for the args. The args are a hash ref of context
arguments that are passed in as a list at construction.
=cut
sub args {
return $_[0]->{args};
}
=head2 message
Builds the message string from the template.
=cut
sub message {
my ($self) = @_;
return sprintf $self->template,
map { $self->args->{$_} } @{ $self->context };
}
=head2 stringify
Stringifies the error to a log message, including the message,
category and wiki_page.
=cut
sub stringify {
my ($self) = @_;
return sprintf qq{%s : %s\nMore info: %s}, $self->category,
$self->message, $self->wiki_page;
}
=head1 AUTHOR
simbabque (some guy on StackOverflow)
=cut
实际单元测试
现在要对此进行测试,区分行为和数据非常重要。 行为包括代码中定义的所有访问者,以及new
,message
和stringify
等更有趣的潜在客户。
我为此示例创建的测试文件的第一部分包括这些。它创建了一个假的错误结构$example_error
,并使用它来检查构造函数是否可以处理正确的参数,缺少或多余的参数,访问者返回正确的东西,以及message
和{{1两者都创建了正确的内容。
请记住,在更改代码时(特别是几个月后),这些测试主要是安全网。如果您在错误的地方意外更改了某些内容,则测试将失败。
stringify
缺少一些特定的测试用例。如果您使用Devel::Cover之类的指标工具,则值得注意的是,完全覆盖并不意味着涵盖了所有可能的情况。
测试您的错误数据质量
现在,本例中值得讨论的第二部分是ErrorLibrary中错误模板的正确性。有人可能会在以后意外混淆某些内容,或者可能会在消息中添加新占位符,但不会添加到上下文数组中。
理想情况下,以下测试代码将放在自己的文件中,并且只有在完成某个功能时才会运行,但为了说明的目的,这只是在上面的代码块之后继续,因此两个第一级{{1 }}第
您问题的主要部分是关于测试用例的列表。我认为这非常重要。您希望测试代码干净,易于阅读,甚至更易于维护。测试经常兼作文档,没有什么比更改代码更烦人,然后试图弄清楚测试是如何工作的,这样你就可以更新它们。所以永远记住这一点:
现在让我们来看看错误的测试。
package main; # something like 01_foo.t
use strict;
use warnings;
use Test::More;
use Test::Exception;
use LWP::Simple 'head';
subtest 'Functionality of Error' => sub {
my $example_error = {
category => 'Connection Error',
template => 'Could not ping switch %s in %s seconds.',
context => [qw(switch_ip timeout)],
fatal => 1,
wiki_page => 'http://example.org',
};
# happy case
{
my $e = Error->new(
$example_error,
timeout => 30,
switch_ip => '127.0.0.1'
);
isa_ok $e, 'Error';
can_ok $e, 'category';
is $e->category, 'Connection Error',
q{... and it returns the correct value};
can_ok $e, 'template';
is $e->template, 'Could not ping switch %s in %s seconds.',
q{... and it returns the correct values};
can_ok $e, 'context';
is_deeply $e->context, [ 'switch_ip', 'timeout' ],
q{... and it returns the correct values};
can_ok $e, 'is_fatal';
ok $e->is_fatal, q{... and it returns the correct values};
can_ok $e, 'message';
is $e->message, 'Could not ping switch 127.0.0.1 in 30 seconds.',
q{... and the message is correct};
can_ok $e, 'stringify';
is $e->stringify,
"Connection Error : Could not ping switch 127.0.0.1 in 30 seconds.\n"
. "More info: http://example.org",
q{... and stringify contains the right message};
}
# not enough arguments
throws_ok( sub { Error->new( $example_error, timeout => 1 ) },
qr/switch_ip/, q{Creating without switch_ip dies} );
# too many arguments
lives_ok(
sub {
Error->new(
$example_error,
timeout => 1,
switch_ip => 2,
foo => 3
);
},
q{Creating with too many arguments lives}
);
};
它基本上有一个测试用例数组,其中一个用于ErrorLibrary导出的每个可能的错误常量。它具有名称,用于加载正确的错误并识别TAP输出中的测试用例,运行测试所需的参数以及预期的最终输出。我只包含消息以保持简短。
如果在不更改文本的情况下在ErrorLibrary(或删除)中修改了错误模板名称,则对象实例化周围的subtest
将失败,因为该名称未导出。这是一个很好的加分。
但是,如果在没有测试用例的情况下添加了新错误,则不会捕获。一种方法是查看subtest 'Correctness of ErrorList' => sub {
# these test cases contain all the errors from ErrorList
my @test_cases = (
{
name => 'ERROR_WIFI_CABLE_TOO_SHORT',
args => {
length => 2,
},
message => 'A WiFi cable of 2 meters is too short.',
},
{
name => 'ERROR_CABLE_HAS_WRONG_COLOR',
args => {
router => 'foo',
color => 'red',
},
message => 'You cannot connect to foo using a red cable.',
},
{
name => 'ERROR_I_AM_A_TEAPOT',
args => {
ip => '127.0.0.1',
},
message => 'The device at 127.0.0.1 is a teapot.',
},
);
# use_ok 'ErrorList'; # only use this line if you have files!
ErrorList->import; # because we don't have a file ErrorList.pm
# in the file system
pass 'ErrorList used correctly'; # remove if you have files
foreach my $t (@test_cases) {
subtest $t->{name} => sub {
# because we need to use a variable to get to a constant
no strict 'refs';
# create the Error object from the test data
# will also fail if the name was not exported by ErrorList
my $e;
lives_ok(
sub { $e = Error->new( &{ $t->{name} }, %{ $t->{args} } ) },
q{Error can be created} );
# and see if it has the right values
is $e->message, $t->{message},
q{... and the error message is correct};
# use LWP::Simple to check if the wiki page link is not broken
ok head( $e->wiki_page ), q{... and the wiki page is reachable};
};
}
};
done_testing;
命名空间中的符号表,但这对于这个答案的范围来说有点太高级了。
它还使用LWP::Simple对每个wiki URL执行lives_ok
HTTP请求,以查看是否可以访问这些请求。这也有很好的好处,如果你在构建它时运行它有点像监视工具。
将所有内容整合在一起
最后,这是在没有main
的情况下运行时的TAP输出。
HEAD