目录

lumen 框架

BaseTestCase

范例:旧版 lumen 框架。

lumen 框架中 TestCase 类,继承了 PHPUnit_Framework_TestCase,基本用法与 phpunit 的用法一致。

为了隔离框架的 TestCase,我们创建了一个 BaseTestCase 类,继承框架的 TestCase,并实现其指定的 createApplication() 方法,创建 lumen 应用,准备应用的单元测试环境。

<?php
namespace tests;
class BaseTestCase extends \Laravel\Lumen\Testing\TestCase
{

    public function createApplication()
    {
        $this->baseUrl = env('APP_URL');
        return require __DIR__ . '/../bootstrap/app.php';
    }
}
  • 所有的单测文件都继承 BaseTestCase。

  • createApplication() 方法是 lumen 单测框架要求实现的抽象方法,用于模拟创建一个\Laravel\Lumen\Application

    lumen 框架中的单测类,继承 BaseTestCase 类,将创建 lumen app,并自动加载 bootstrap/app.php,确保单元测试过程中能正常加载框架需要的文件。

phpunit.xml

phpunit.xml 文件,用于配置 phpunit 单元测试框架,phpunit 启动时会加载 phpunit.xml 文件中的配置信息,并执行测试用例。

详见【phpunit.xml 配置说明】

单测引导文件

在 BaseTestCase 中我们按 lumen 单测框架要求实现了 createApplication() 方法,所以我们未在 phpunit.xml 中指定引导文件,比如:

bootstrap="tests/bootstrap.php"

YII 框架

BaseTestCase

由于我们一些项目的 php 版本低,且使用了比较低的 phpunit 的版本。phpunit 5.4 之前的版本使用 PHPUnit\Framework\PHPUnit_Framework_TestCase ,之后的版本使用 PHPUnit\Framework\TestCase,为了隔离这一层命名的变化,我们补充一个 BaseTestCase,方便后续快速调整使用新的 phpunit。

<?php

namespace tests;

use PHPUnit_Framework_TestCase;

class BaseTestCase extends PHPUnit_Framework_TestCase
{
    
}

phpunit.xml

phpunit.xml 文件,用于配置 phpunit 单元测试框架,phpunit 启动时会加载 phpunit.xml 文件中的配置信息,并执行测试用例。

详见【phpunit.xml 配置说明】

参考:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="tests/bootstrap.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory suffix=".php">src/</directory>
        </whitelist>
    </filter>
    <logging>
        <log type="coverage-html" target="build/code-coverage" charset="UTF-8"/>
    </logging>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
    </php>
</phpunit>

单测引导文件

上面 phpunit.xml 文件中配置了引导文件,即:

bootstrap="tests/bootstrap.php"

我们创建 tests/bootstrap.php,来自于 api\web\index.php,用于加载框架的常量、配置文件、业务类文件等,并创建应用实例,但并不运行应用,使得单测过程中,正常使用到框架的常量、配置文件、业务类文件等。

<?php

// 该文件是 phpunit 的引导文件。

// 定义常量
defined('YII_API_PATH') or define('YII_API_PATH', realpath(__DIR__.'/../api'));
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');

// 加载 Composer 自动加载
require __DIR__ . '/../vendor/autoload.php';

// 优先加在的配置出现重复名称后优先级高,hot.env 优先加在避免上线产生影响。
// Bootstrap for hot.env
try {
    (new \Dotenv\Dotenv(__DIR__ . '/../', '.env_hot'))->load();
} catch (\Dotenv\Exception\InvalidPathException $e) {
    // cant load hot.env
}

// 加载环境变量
try {
    (new \Dotenv\Dotenv(__DIR__ . '/../', '.env.test'))->load();
} catch (\Dotenv\Exception\InvalidPathException $e) {
    // 如果找不到测试环境文件,尝试加载普通环境文件
    try {
        (new \Dotenv\Dotenv(__DIR__ . '/../'))->load();
    } catch (\Dotenv\Exception\InvalidPathException $e) {
        // 如果都找不到,使用默认值
        $_ENV['YII_DEBUG'] = true;
        $_ENV['YII_ENV'] = 'test';
    }
}

// 加载 Yii 框架
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';

// 加载通用引导文件
require __DIR__ . '/../common/config/bootstrap.php';
require __DIR__ . '/../api/config/bootstrap.php';

// 合并配置文件
$config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../common/config/main.php'),
    require(__DIR__ . '/../api/config/main.php')    
);

// 创建应用实例但不运行
new yii\web\Application($config); 

单测范例

单测环境搭建好后,我们在 tests 目录下创建一个测试用例文件,测试单例执行效果:

<?php

namespace tests;

class DemoTest extends BaseTestCase
{
    public function testDemo()
    {
        $cases = [
            [
                "input" => "123",
                "expected" => "123"
            ],
            [
                "input" => "124",
                "expected" => "124"
            ],
            [
                "input" => "125",
                "expected" => "125"
            ],
        ];

        foreach ($cases as $case) {
            $this->assertEquals($case["expected"], $case["input"]);
        }
    }
}

phpunit 对 php 版本的要求

以下是 PHPUnit 5.0 及以上版本对 PHP 版本的要求:

PHPUnit 版本 PHP 版本要求
PHPUnit 5.0 - 5.7 PHP 5.6 - PHP 7.1
PHPUnit 6.0 - 6.4 PHP 7.0 - PHP 7.2
PHPUnit 7.0 - 7.5 PHP 7.1 - PHP 7.3
PHPUnit 8.0 - 8.5 >= PHP 7.2
PHPUnit 9.0 - 9.6 >= PHP 7.3
PHPUnit 10.0 - 10.5 >= PHP 8.1
PHPUnit 11.0 - 11.5 >= PHP 8.2
PHPUnit 12.0 - 12.5 >= PHP 8.3

常见问题

找不到业务类

大多是因为 composer 没有重新生成自动加载文件导致。

从错误信息 Error : Class 'App\Services\Pipo\SlogService' not found,可以推断出在测试文件 SlogServiceTest.php 中引用的 SlogService 类没有被正确加载。一些可能的原因和解决方法:

  1. 命名空间问题

    • 确认 SlogService 类所在的文件路径和命名空间是否正确。
    • 例如,如果 SlogService 类位于 app/Services/Pipo/SlogService.php,则文件内容应如下所示:
      <?php
      
      namespace App\Services\Pipo;
      
      class SlogService {
          // 类内容
      }
      
  2. 自动加载问题

    • 确保 Composer 的自动加载文件已生成并包含在项目中。运行 composer dump-autoload 命令来重新生成自动加载文件。
    • 确认 composer.json 文件中的 autoload 部分正确配置了 App\Services\Pipo 命名空间的路径:
      "autoload": {
          "psr-4": {
              "App\\": "app/"
          }
      }
      
  3. 文件路径问题

    • 确认 SlogService.php 文件确实存在于 app/Services/Pipo/ 目录下。
  4. 测试环境配置问题

    • 确认测试环境配置正确,特别是 bootstrap 文件中是否正确加载了 Composer 的自动加载文件。通常在 tests/bootstrap.phptests/TestCase.php 中会有类似以下的代码:
      require __DIR__ . '/../vendor/autoload.php';
      
  5. 更新 composer 文件

    composer dump-autoload
    

通过以上步骤,应该能够解决 Class 'App\Services\Pipo\SlogService' not found 的问题。如果问题仍然存在,请检查是否有其他配置或环境问题。

参考

PHPUnit Manual — PHPUnit 8.5 Manual


9ong@TsingChan 文章内容由 AI 辅助生成。