如何为具有哈希参数的方法编写Perl单元测试?

时间:2018-05-10 09:30:09

标签: perl unit-testing subroutine

此子例程将通过对其应用正则表达式从环境中获取API主机名。

Parameters : 
environment (mandatory) - http://m5devacoe01.gcsc.att.com:8174/
Returns :
host : m5devacoe01.gcsc.att.com:28174
########################################################
# Subroutine: _get_api_host_from_environment
#
# Fetches the api host name from environment by applying regex on it
#
# Parameters : 
# environment (mandatory) - http://m5devacoe01.gcsc.att.com:8174/
#                    
# Returns :
# host : m5devacoe01.gcsc.att.com:28174
#####################################################


 sub _get_api_host_from_environment {
        my ($self, %args) = @_;

        $self->missing_params(\%args, qw! environment!)
            and return;

        $args{environment} =~ /([^:\/]+):?([^\/]*)/; #  http://m5devacoe01.gcsc.att.com:8174/
        my $host = $1; # m5devacoe01.gcsc.att.com
        my $port = $2; # 8174

        # If port is provided it means that it's a development server request. So, append 2 in the port
        if ($port) {
            $host .= ':2' . $port;
        } else {
            $host .= ':4040';
        }

        return $host;
    }

如何对此方法进行单元测试?

1 个答案:

答案 0 :(得分:3)

您的代码实际上并不复杂。您实际上并未访问任何非常具体的内容。这只是一个参数哈希。正确的做法是测试这些情况:

  • 在没有args的情况下被召唤
  • 使用错误的args调用
  • 使用与模式不匹配的值调用(将失败,您不会考虑该模式)
  • 使用带有端口
  • 的尾部斜杠的URL调用 使用URL调用
  • 而不使用端口
  • 尾随斜杠
  • 使用带有尾部斜杠的URL调用
  • 使用URL调用,没有尾部斜杠而没有端口

这些测试都非常简单。

use Test::More;
use strict;
use warnings;

my $foo = Foo->new;

is( $foo->_get_api_host_from_environment(),
    undef, 'return undef without arguments' );
is( $foo->_get_api_host_from_environment( foo => 123 ),
    undef, 'return undef with missing environment' );

{
    local $TODO = 'this check is not implemented yet';

    is(
        $foo->_get_api_host_from_environment(
            environment => 'this is not a URL'
        ),
        undef,
        'environment is not a URL'
    );
}

is(
    $foo->_get_api_host_from_environment(
        environment => 'http://example.org:8174/'
    ),
    'example.org:28174',
    'port number and trailing slash prefixes 2 to the port'
);
is(
    $foo->_get_api_host_from_environment(
        environment => 'http://example.org:8174'
    ),
    'example.org:28174',
    'port number w/o trailing slash prefixes 2 to the port'
);

is(
    $foo->_get_api_host_from_environment(
        environment => 'http://example.org/'
    ),
    'example.org:4040',
    'no port number and trailing slash adds port 4040'
);
is(
    $foo->_get_api_host_from_environment(
        environment => 'http://example.org'
    ),
    'example.org:4040',
    'no port number w/o trailing slash adds port 4040'
);

done_testing;

请注意我的测试如何都有描述性名称。好的测试可以作为未来维护代码的开发人员的文档(可能是一年中的代码),因此请确保您实际谈论了在描述中测试的内容和原因。

其中有$TODO表示尚未构建对错误网址的检查。该测试将失败,但测试套件无论如何都会通过,因为预计会失败。

为了让这些测试通过,我需要在你的sub周围添加一些代码。这就是我所做的。

package Foo;
use strict;
use warnings;

sub new { return bless {}, $_[0] }

sub missing_params {
    my $self = shift;
    my $args = shift;

    foreach my $param (@_) {
        return 1 unless exists $args->{$param};
    }

    return;
}

sub _get_api_host_from_environment {
    my ( $self, %args ) = @_;

    $self->missing_params( \%args, qw! environment! )
      and return;

    $args{environment} =~ m{(?:https?://)?([^:\/]+):?([^\/]*)}
      ;    #  http://m5devacoe01.gcsc.att.com:8174/
    my $host = $1;    # m5devacoe01.gcsc.att.com
    my $port = $2;    # 8174

# If port is provided it means that it's a development server request. So, append 2 in the port
    if ($port) {
        $host .= ':2' . $port;
    }
    else {
        $host .= ':4040';
    }

    return $host;
}

您会注意到我是如何更改正则表达式的。那是因为你的实际上是错的,没有做你的文档所说的代码。新模式引入了https而不是http作为方案的选项,我没有进行测试,也没有完整的方案。我没有包含这些案例的测试,但它们也应该存在。

实际使用URI模块可能更为明智。它允许您直接修改端口,并为您提供免费的健全性检查。

sub _get_api_host_from_environment {
    my ( $self, %args ) = @_;

    $self->missing_params( \%args, qw! environment! )
      and return;

    my $uri = URI->new( $args{environment} ) or return;
    return unless $uri->scheme && $uri->scheme =~ 'http'; # includes https

    my $port = $uri->port;    # 8174

# If port is provided it means that it's a development server request. So, append 2 in the port
    if ($port != 80 and $port != 443) {
        $uri->port( '2' . $port );
    }
    else {
        $uri->port('4040');
    }

    # return $uri->canonical; # this would return http://example.org:28174/
    return $uri->host_port;
}

通过此测试,相同的单元测试仍将通过。