2016-04-25 74 views
0

這只是我對錯誤運行的一個測試的一個例子。我想編輯這個以便我可以遍歷錯誤列表。我會把這些錯誤放入散列並創建一個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

請說明問題所在。你在問如何構建單元測試以避免代碼重複?你正在談論'流程',從你以前的問題中我相信這是一種方法。爲什麼會進入單元測試? – simbabque

+0

沒有抱歉,我只是想知道如何設置我的測試,以便能夠測試一個錯誤,我可以使用for循環或類似的方式運行多個錯誤的測試。我並不是故意將這個詞彙加入其中。我只是認爲我應該用'data = [input => UNABLE_TO_PING_SWITCH_ERROR,output =>「無法在30秒內ping開關192.192.0.0]創建一個列表;''對於每個錯誤,是否可以這樣做?進入一個數據數組並通過它循環 –

+0

你會得到我想要做的嗎?也許我會錯誤的方式嗎? –

回答

1

經過長時間的聊天討論,我提出了以下方法來進行單元測試,以及輕鬆重構實際代碼以使事情變得更容易。

變化我做

我已經重構的代碼從模板創建錯誤消息不使用Template的方式,因爲它是清楚your previous question,這是一個有點矯枉過正。

它現在使用sprintfTimeout 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); 

上下文是與預計在施工該密鑰的列表的數組引用。

的重構(簡體)Error類

這一類包括POD來解釋它做什麼。重要的方法是構造函數messagestringify

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都會創建正確的內容。

請記住,這些測試主要是更改代碼時的安全網(尤其是在幾個月後)。如果你不小心改變了錯誤的地方,測試將會失敗。

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} 
    ); 

}; 

有一些特定的測試案例丟失。如果您使用像Devel::Cover這樣的度量工具,值得注意的是全覆蓋並不意味着涵蓋所有可能的情況。

測試您的錯誤數據質量

現在的第二部分,是值得覆蓋在這個例子是在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; 

它基本上有一個測試用例數組,其中每個可能的錯誤常量由ErrorLibrary導出。它具有名稱,用於加載正確的錯誤,並在TAP輸出中標識測試用例,運行測試所需的參數以及預期的最終輸出。我只包含消息以保持簡短。

如果在ErrorLibrary(或刪除)中修改錯誤模板名稱而未更改文本,圍繞對象實例化的lives_ok將失敗,因爲該名稱未導出。這是一個很好的補充。

但是,如果在沒有測試用例的情況下添加新的錯誤,它將不會被捕獲。一種方法是查看名稱空間main中的符號表,但這對於此答案的範圍來說有點太高級了。

它還會用LWP::Simple對每個wiki URL執行HEAD HTTP請求以查看它們是否可到達。這也有很好的好處,如果你在構建時運行它,它就像一個監視工具。

把它放在一起

最後,這裏是TAP輸出,當沒有prove運行。

# 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 
+0

我們可以在任何時候開放討論嗎?我有幾個關於代碼的問題。我的功能測試工作正常,但我的正確性測試給出了一些錯誤。 –

+1

@PaulRussell進入舊的測試 – simbabque

+0

我將在接下來的幾天大部分時間在線。謝謝 –

相關問題