首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >我如何修改这段代码,以便我可以使用它来迭代和测试一个错误列表?

我如何修改这段代码,以便我可以使用它来迭代和测试一个错误列表?
EN

Stack Overflow用户
提问于 2016-04-25 12:03:49
回答 1查看 60关注 0票数 0

这只是我在错误上运行的一个测试的一个例子。我想要编辑它,以便我可以循环通过一个错误列表。我会把这些错误放到散列中并创建一个for循环来迭代它们吗?我不知道到底是怎么做到的。我将显示下面的测试和错误库。只是需要一个小例子才能让我开始工作。

测试文件:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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;

我只是不知道如何创建我的名单。是否应该创建一个包含要测试的输入、处理和输出的列表。我有点糊涂。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-04-26 19:02:49

在chat中进行了长时间的讨论之后,我提出了以下方法来进行单元测试,以及对实际代码进行轻量级的重构,以使事情变得更容易一些。

我做的更改

我重新构造了代码从模板中创建错误消息的方式,使其不使用模板,因为从你之前的问题中可以清楚地看出,这有点过分。

它现在使用的是sprintf,它的模式很简单,比如Timeout after %s seconds。在整个示例中,我都是有意使用%s的,因为这些示例中从来没有任何类型检查,但当然可以添加它。此消息的参数作为从第二个参数开始的键/值对的列表传递给构造函数。

代码语言:javascript
运行
复制
my $e = Error->new(CONSTANT, foo => 'bar');

示例ErrorLibrary

第一个参数CONSTANT仍然来自错误库。我列举了以下简化的例子。

代码语言:javascript
运行
复制
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);

上下文是一个数组引用,其中包含在构造时预期的键列表。

重构(简化)错误类

这个类包括POD来解释它所做的事情。重要的方法是构造函数、messagestringify

代码语言:javascript
运行
复制
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

实际单元测试

现在要测试这一点,区分行为和数据是很重要的。该行为包括代码中定义的所有访问器,以及更有趣的子类,如newmessagestringify

我为本例创建的测试文件的第一部分包括以下内容。它创建一个假错误结构$example_error,并使用它检查构造函数是否能够处理正确的参数、丢失的或多余的参数、访问器是否返回正确的内容,以及messagestringify是否都创建了正确的内容。

请记住,在更改代码时(特别是在几个月后),这些测试主要是一个安全网。如果你不小心在错误的地方改变了一些东西,测试就会失败。

代码语言:javascript
运行
复制
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中错误模板的正确性。稍后可能有人不小心混淆了一些东西,或者消息中可能添加了一个新的占位符,但没有添加到上下文数组中。

理想情况下,下面的测试代码将放在自己的文件中,并且只在您完成某项功能时运行,但为了说明起见,这只会在上面的代码块之后继续,因此出现了两个一级subtest

你问题的主要部分是关于测试用例的清单。我认为这是非常重要的。您希望您的测试代码是干净的,易于阅读,甚至更容易维护。测试通常是文档化的两倍,没有什么比更改代码更烦人的了,然后试图找出测试是如何工作的,这样您就可以更新它们。所以永远记住这一点:

测试也是生产代码!

现在让我们来看看错误的测试。

代码语言:javascript
运行
复制
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;

它基本上有一个测试用例数组,对于ErrorLibrary导出的每个可能的错误常量都有一个用例。它具有名称,用于加载正确的错误并标识TAP输出中的测试用例、运行测试所需的参数以及预期的最终输出。我只写了一条短信以保持简短。

如果错误模板名在ErrorLibrary中被修改(或删除)而不更改文本,则对象实例化周围的lives_ok将失败,因为该名称没有导出。这是个不错的好处。

但是,如果在没有测试用例的情况下添加了新错误,则不会捕获。这方面的一种方法是查看main命名空间中的符号表,但对于这个答案的范围来说,这有点太高级了。

它还可以使用LWP::简单对每个wiki执行HEAD HTTP请求,以查看这些请求是否可访问。这也有一个很好的好处,如果您在构建时运行它,它的作用有点像一个监视工具。

把一切都聚集在一起,

最后,这是在没有prove的情况下运行时的抽头输出。

代码语言:javascript
运行
复制
    # Subtest: Functionality of Error
    ok 1 - An object of class 'Error' isa 'Error'
    ok 2 - Error->can('category')
    ok 3 - ... and it returns the correct value
    ok 4 - Error->can('template')
    ok 5 - ... and it returns the correct values
    ok 6 - Error->can('context')
    ok 7 - ... and it returns the correct values
    ok 8 - Error->can('is_fatal')
    ok 9 - ... and it returns the correct values
    ok 10 - Error->can('message')
    ok 11 - ... and the message is correct
    ok 12 - Error->can('stringify')
    ok 13 - ... and stringify contains the right message
    ok 14 - Creating without switch_ip dies
    ok 15 - Creating with too many arguments lives
    1..15
ok 1 - Functionality of Error
    # Subtest: Correctness of ErrorList
    ok 1 - ErrorList used correctly
        # Subtest: ERROR_WIFI_CABLE_TOO_SHORT
        ok 1 - Error can be created
        ok 2 - ... and the error message is correct
        ok 3 - ... and the wiki page is reachable
        1..3
    ok 2 - ERROR_WIFI_CABLE_TOO_SHORT
        # Subtest: ERROR_CABLE_HAS_WRONG_COLOR
        ok 1 - Error can be created
        ok 2 - ... and the error message is correct
        ok 3 - ... and the wiki page is reachable
        1..3
    ok 3 - ERROR_CABLE_HAS_WRONG_COLOR
        # Subtest: ERROR_I_AM_A_TEAPOT
        ok 1 - Error can be created
        ok 2 - ... and the error message is correct
        ok 3 - ... and the wiki page is reachable
        1..3
    ok 4 - ERROR_I_AM_A_TEAPOT
    1..4
ok 2 - Correctness of ErrorList
1..2
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36840048

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档