thinkphp5.0 使用 phpunit 进行单元测试

PHP 2020-02-21 452 次浏览 次点赞

起步

thinkphp5.0 是 tp 发展路线第一个支持 composer 的。tp 有自己的载入机制,composer 也有自己的载入机制。

官方提供了 think-testing 组件可以通过 composer require topthink/think-testing 1.* 安装,但我对这个测试组件不是很满意,原因是它每个测试都是模拟成 http 请求。这也就意味着对于一些特定函数,还得封装到控制器中或者路由,该控制器还得控制它生产环境不能对外开放。

另一个原因是可读性会变差,在单元测试文件中只是能看到请求体和响应体,具体测试的函数,测试的细节都看不到。于是我尝试寻找可以独立测试tp或者tp项目,测试颗粒更细的测试单元。(当然是可行的,不然我也不会写这篇文章)

使用 phpunit 单元测试

安装:

composer require --dev phpunit/phpunit

进行安装,我安装的版本是 8.5.2 ,项目是运行在 php7.2 环境的。tp 和 composer 都有自己的载入机制,于是我决定都试一试。

使用 composer 载入机制

composer 的载入主要是针对 vendor 文件内的第三方依赖。 但对于 vendor 外的文件需要指定载入空间映射的文件路径,在 composer.json 中添加 autoload 字段:

{
    "require-dev": {
        "phpunit/phpunit": "8.5.2"
    },
    "autoload": {           # 设置命名空间映射
        "psr-4": {
            "app\\": "application/",
            "basic\\": "extend/basic",
            "traits\\": "extend/traits",
            "tests\\": "tests/"
        }
    }
}

通过 composer dumpautoload 让配置生效。单元测试的文件按规范放在项目目录 tests 文件夹中:

<?php
// tests/ThinkableTest.php
namespace tests;

use app\core\model\user\User;
use PHPUnit\Framework\TestCase;
use think\Config;
use think\Db;

// 初始化app
define('APP_PATH', dirname(__DIR__) . '/application/');
define('VENDOR_PATH', APP_PATH . 'vendor_fake' . DIRECTORY_SEPARATOR); // 让tp不用自己的载入机制,而使用composer的autoload

include dirname(__DIR__) . '/thinkphp/base.php';
Config::load(APP_PATH . '/database.php', 'database');

class ThinkableTest extends TestCase
{
    public function testFrameworkVersion() {   // 检查 tp 版本
        $this->assertTrue(version_compare(THINK_VERSION, '5.0', '>='));
        $this->assertTrue(version_compare(THINK_VERSION, '6.0', '<'));
    }

    public function testDbConnect() {  // 数据库是否能连接
        $tables_count = Db::execute('show tables');
        $this->assertTrue($tables_count > 0);
    }

    public function testModelUser() {  // 模型是否可用
        $model = new User();
        $user = $model->limit(1)->find();
        $this->assertTrue($user != null);
    }
}

运行结果:

D:\wamp64\bin\php\php7.2.4\php.exe E:/workspace/gift_mall_official/vendor/phpunit/phpunit/phpunit --no-configuration tests\ThinkableTest E:\workspace\gift_mall_official\tests\ThinkableTest.php --teamcity
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Time: 89 ms, Memory: 6.00 MB

OK (3 tests, 4 assertions)

Process finished with exit code 0

能运行成功归功于这几行载入 tp 的依赖,这几行代码可以提取到公共文件中:

// 初始化app
define('APP_PATH', dirname(__DIR__) . '/application/');
define('VENDOR_PATH', APP_PATH . 'vendor_fake' . DIRECTORY_SEPARATOR); // 让tp不用自己的载入机制,而使用composer的autoload

include dirname(__DIR__) . '/thinkphp/base.php';
Config::load(APP_PATH . '/database.php', 'database');

其中定义 VENDOR_PATH 并设置了一个不存在的路径,是为了使用 composer 自身的自动载入。

模拟请求API测试

模拟请求就可以借助 think-testing 组件了,但我还是不打算用它,我选择了 GuzzleHttp 依赖进行,以下是测试样例:

<?php
// tests/SimpleAPITest.php
namespace tests;

use GuzzleHttp\Client;
use PHPUnit\Framework\TestCase;

class SimpleAPITest extends TestCase {
    private $client = null;

    public function __construct(?string $name = null, array $data = [], string $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
        $this->client = new Client([
            'base_uri' => 'http://localhost'
        ]);
    }

    public function testWebIndex() {
        $resp = $this->client->get('/');
        $this->assertEquals(200, $resp->getStatusCode());
    }
}

更多 GuzzleHttp 的使用方法查看文档:https://guzzle-cn.readthedocs.io/zh_CN/latest/

总结

本文介绍了如何用原生的 phpunit 针对 thinkphp5.0 进行单元测试,这里重点是如何载入tp框架又要避免重复引入依赖。

使用tp自身的指令进行单元测试是未来发展的趋势,tp6.0 就比较的成熟。而对于tp5.0 ,它是第一次对composer的尝试,我还是想用自己的整合方式进行单元测试。


本文由 hongweipeng 创作,采用 署名-非商业性使用-相同方式共享 3.0,可自由转载、引用,但需署名作者且注明文章出处。

赏个馒头吧