2

我对单元测试特别是 PHPUnit 很陌生,所以如果这是一个简单的问题,请原谅我。我做了一些谷歌搜索,但我不知道要搜索什么。

所以我有一个功能:

function convert_timezone($dt, $tzFrom, $tzTo, $format) {
    $newDate = '';

    $tzFromFull = timezone_name_from_abbr($tzFrom);
    $tzToFull = timezone_name_from_abbr($tzTo);

    if( $tzFromFull != $tzToFull ) {
        $dtFrom = new DateTimeZone($tzFromFull);
        $dtTo = new DateTimeZone($tzToFull);

        try {
            // find the offsets from GMT for the 2 timezones
            $current = new DateTime(date('c',$dt));
            $offset1 = $dtFrom->getOffset($current);
            $offset2 = $dtTo->getOffset($current);
            $offset = $offset2 - $offset1;

            // apply the offset difference to the current time
            $newDate = date($format, $current->format('U') + $offset);
        } catch (Exception $e) {
            $newDate = date($format.' (T)', $dt);
        }
    } else {
        $newDate = date($format, $dt);
    }

    return $newDate;
}

这是测试功能:

function test_convert_timezone() {
   $dt = mktime(0,0,0,1,1,2000);
   $tzFrom = 'CDT';
   $tzTo = 'EDT';
   $format = 'd/m/Y g:i a';
   $result = convert_timezone($dt, $tzFrom, $tzTo, $format);
   $this->assertEquals($result, '01/01/2000 1:00 am');
}

当我直接在 Netbeans 中运行测试时,测试通过了。但是当我使用命令行选项生成代码覆盖率报告时,它告诉我这个函数没有被完全覆盖,因为测试没有遇到异常。

我知道如果我可以更改原始函数,我可以强制它抛出错误。但我不能那样做。我需要一种方法,在测试函数中传递一些东西,这将使被测试的函数遇到异常。我从哪说起呢?

4

1 回答 1

1

就代码而言,您几乎无法测试该分支。

您的 try-catch 块实际上并没有捕获任何异常。(date抛出一个未被捕获的错误。 getOffset也没有)具有讽刺意味的是new DateTimeZone,如果你传入一个不合适的时区常量 DateTimeZone::__construct()。因此,如果您可以重构它并传入未列为有效的时区,您可以获得异常(您应该这样做,因为您似乎打算在此处捕获异常)。

function convert_timezone($dt, $tzFrom, $tzTo, $format) {
    $newDate = '';

    $tzFromFull = timezone_name_from_abbr($tzFrom);
    $tzToFull = timezone_name_from_abbr($tzTo);

    if( $tzFromFull != $tzToFull ) {
        try {
            $dtFrom = new DateTimeZone($tzFromFull);
            $dtTo = new DateTimeZone($tzToFull);


            // find the offsets from GMT for the 2 timezones
            $current = new DateTime(date('c',$dt));
            $offset1 = $dtFrom->getOffset($current);
            $offset2 = $dtTo->getOffset($current);
            $offset = $offset2 - $offset1;

            // apply the offset difference to the current time
            $newDate = date($format, $current->format('U') + $offset);
        } catch (Exception $e) {
            $newDate = date($format.' (T)', $dt);
        }
    } else {
        $newDate = date($format, $dt);
    }

    return $newDate;
}

然后测试将是

function test_convert_timezone() {
   $dt = mktime(0,0,0,1,1,2000);
   $tzFrom = 'foo';
   $tzTo = 'EDT';
   $format = 'd/m/Y g:i a';
   $result = convert_timezone($dt, $tzFrom, $tzTo, $format);
   $this->assertEquals($result, '01/01/2000 1:00 am');
}

在这个测试中,timezone_name_from_abbr将返回false这将导致DateTimeZone构造函数抛出一个异常到达你的分支。

IMO,您希望避免构造要在函数中使用的对象。对于此代码,我将传入两个 DateTimeZone 对象以及一个 DateTime 对象并对它们执行操作。将这些其他对象的创建移动到不同的函数/类中。通过这种方式,您可以模拟 DateTimeZone 和 DateTime 对象本身,并对正在发生的事情进行更多控制。这段代码还不错,但它可能会在尝试测试其他东西时引起麻烦。

此外,请注意在代码中捕获通用异常。如果您在 try-catch 块中使用模拟对象,您的代码可以捕获 FailedTestException(当未使用正确参数或类似参数调用模拟时抛出),这将被您的代码捕获。让它看起来像你的测试通过了,而实际上却没有。由于代码和缺乏模拟,这里再次不是问题,但我想让你意识到这一点。

于 2013-05-04T01:21:58.383 回答