定义单元测试

单元测试是专门测试解决方案代码单元的程序。只需将其视为一个测试函数的程序,而不依赖于项目中的其他对象。

例如,如果您有一个名为 calculateTotal($a, $b, $c) 的函数,那么您可以为它编写一个名为 testCanCalculateTotal() 的单元测试函数。该单元测试的任务是根据项目规范中定义的业务规则,验证 calculateTotal($a, $b, $c) 函数是否返回预期结果。

在本例中,我们假设 calculateTotal 函数的预期行为是获取 $a$b$c 三个参数的总和。

让我们创建一个单元测试示例和解决方案代码。在我们的开发容器中创建以下文件:

codebase/symfony/tests/Unit/CalculationTest.php
<?php
namespace App\Tests\Unit;
use App\Example\Calculator;
use PHPUnit\Framework\TestCase;

class CalculationTest extends TestCase
{
    public function testCanCalculateTotal()
    {
        // Expected result:
        $expectedTotal = 6;

        // Test data:
        $a = 1;
        $b = 2;
        $c = 3;

        $calculator = new Calculator();
        $total = $calculator->calculateTotal($a, $b, $c);
        $this->assertEquals($expectedTotal, $total);
    }
}

测试类名称需要以 Test 为后缀,它扩展了 PHPUnit\Framework\TestCase 类。通过这样做,我们现在正在使用 PHPUnit 库。

接下来,让我们尝试运行这个单元测试,看看会发生什么。在容器内运行以下命令。有关如何执行所有这些操作的说明请参见 第 3 章,使用 Docker 容器设置我们的开发环境

/var/www/html/symfony# php bin/phpunit –filter testCanCalculateTotal

结果将是一个错误:

image 2023 10 23 14 59 49 382
Figure 1. Figure 5.1 – Fail 1 (class not found)

我们的单元测试如预期失败了——这很好!您会注意到我们试图实例化一个不存在的类,所以现在让我们创建该类并编写函数来进行计算。在我们之前创建的 codebase/symfony/src/Example/ 目录中创建以下解决方案类:

codebase/symfony/src/Example/Calculator.php
<?php

namespace App\Example;

class Calculator
{
    public function calculateTotal(int $a, int $b, int $c) : int
    {
        return $a + $b - $c;
    }
}

使用 calculateTotal 函数创建解决方案类后,让我们尝试通过运行以下命令再次运行测试:

/var/www/html/symfony# php bin/phpunit –filter testCanCalculateTotal

我们将得到以下失败结果:

image 2023 10 23 15 03 19 098
Figure 2. Figure 5.2 – Fail 2 (incorrect result)

PHPUnit 会告诉我们测试失败的原因。您会注意到它显示:Failed asserting that 0 matches expected 6。为什么会这样? 嗯,这就是发生的事情。

testCanCalculateTotal 单元测试中,我们已将 $expectedTotal 声明为 6。然后,我们调用 calculateTotal 函数并发送以下参数:$a = 1$b = 2$c = 3。如果您收到的规范指示您获得 calculateTotal 函数中三个参数的总和,那么预期结果是 6

然后我们使用 assertEquals PHPUnit 函数,我们告诉 PHPUnit 我们期望预期值将等于计算值,如下行所示:

$this->assertEquals($expectedTotal, $total);

断言是断言或检查测试中的条件是否满足的方法或函数。就像在我们的示例中,我们使用了 assertEquals 方法,尝试将 $expectedTotal 与我们从解决方案代码中收到的实际 $total 进行比较。PHPUnit 断言有很多不同类型,文档可在此处找到: https://phpunit.readthedocs.io/en/9.5/assertions.html

单元测试正确地期望预期结果为 6 - 问题是在解决方案函数中,我们没有遵循预期行为。我们将 $c 减去,而不是加到 $a$b 的和中。如果我们将函数修改如下,测试就会最终通过:

codebase/symfony/src/Example/Calculator.php
public function calculateTotal(int $a, int $b, int $c) : int
{
    return $a + $b + $c;
}

为了得到总数,我们只需要得到三个参数的总和。更新 Calculator.php 文件后,运行以下命令:

php bin/phpunit --filter testCanCalculateTotal

我们现在应该看到以下结果:

image 2023 10 23 15 37 33 272
Figure 3. Figure 5.3 – Correct result

不错!我们终于通过了单元测试!assertEquals 函数确认了 $expectedTotal 现在等于解决方案代码返回的 $total

现在,想象一下有成千上万个这样的测试。如果解决方案代码的行为发生意外变化,就会导致一个或多个单元测试失败。这是非常有价值的。这将帮助开发人员验证他们实施的任何代码变更的稳定性。

要了解有关 PHPUnit 的更多信息,可访问其文档页面 https://phpunit.readthedocs.io/

这是使用单元测试的最基本示例之一,但随着项目的继续,我们将编写更多的单元测试并使用更多的 PHPUnit 功能。

对解决方案代码的测试越多,解决方案的稳定性就越好。因此,接下来我们将研究 PHPUnit 的代码覆盖率分析解决方案。这将帮助我们了解解决方案的测试覆盖率。