前言
在项目开发中,需要使用到ThinkPHP 5,为了编写单元测试,解决了几个难题,特此纪录分享一下。
难点1:TP5自带的单元测试感觉不好用,如何使用纯粹的原生PHPUnit?
在看云上,有TP5官方关于单元测试的使用说明,链接是:https://www.kancloud.cn/manual/thinkphp5/182511
但上面的说明过于简单,对于实际使用帮助有限。
对于一直钟情于自动化单元测试以及PHPUnit原生单元测试的我,决定对此优化一番,引入并在ThinkPHP 5下使用原生PHPUnit。
第一步:准备工作
在tests目录下,创建一个phpunit目录,然后创建两个文件:测试启动文件bootstrap.php和单元测试的配置文件phpunit.xml。
测试启动文件bootstrap.php,可以参考项目的启动文件,复制过来后调整下,例如这样:
<?php
// 定义应用目录
define('APP_PATH', __DIR__ . '/../../application/');
define('APP_DEBUG', true); //开启调试模式
define("APP_STATUS", "tests"); //定义为本地环境
define("RUNTIME_PATH", __DIR__ . "/../../runtime/"); //定义缓存目录
require APP_PATH . '/define.php';
// ThinkPHP 引导文件
// 加载基础文件
require __DIR__ . '/../../thinkphp/base.php';
// 加载应用
\think\Loader::addNamespace('app', APP_PATH);
// 兼容旧版本的PHPUnit
if (!class_exists('PHPUnit_Framework_TestCase')) {
class PHPUnit_Framework_TestCase extends PHPUnit\FrameWork\TestCase {
}
}
// 手动再引入一次测试配置
\think\Config::load(APP_PATH . '/tests/test.php');
// 手动引入框架和应用的函数
require_once APP_PATH . '../thinkphp/helper.php';
require_once APP_PATH . 'common.php';
对于phpunit.xml配置文件,可以这样写:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
bootstrap="./bootstrap.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<php>
<ini name="intl.default_locale" value="en"/>
<ini name="intl.error_level" value="0"/>
<ini name="memory_limit" value="-1"/>
</php>
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./</directory>
<directory suffix="Test.php">./application</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../../application</directory>
</whitelist>
</filter>
</phpunit>
第二步:写个简单的demo
下面编写一个简单的测试用例,试运行一下。在phpunit下创建一个demo目录,并创建TestCaseClass.php文件。此时结构如下:
$ tree ./phpunit
./phpunit
├── bootstrap.php
├── demo
│ └── TestCaseClass.php
└── phpunit.xml
TestCaseClass.php文件代码是:
<?php
use PHPUnit\Framework\TestCase;
namespace tests\demo;
class TestCaseClass extends \PHPUnit_Framework_TestCase
{
public function testHere()
{
$this->assertTrue(true);
}
}
这样编写成功后,就可以试运行了。
第三步:运行一个简单的PHPUnit测试
$ phpunit ./demo/TestCaseClass.php
PHPUnit 5.7.25 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 121 ms, Memory: 10.00MB
OK (1 test, 1 assertion)
后面就可以继续这样编写原生的PHPUnit单元测试啦。
难点2:如何使用PhalApi开源框架的脚本,为TP5项目自动生成测试代码?
下面就要为TP5的项目代码编写单元测试了,但每次都要人工手动重复编写测试代码是一件很累人、很耗时、很低率的工作。有没更好的技能?有!
参考我曾经编写的脚本工具:[PHPUnit]自动生成PHPUnit测试骨架脚本-提供您的开发效率【2015升级版】
现在已经成为PhalApi开源框架内置的脚本命令了。PhalApi是一个专注于接口开发的开源框架,因此我们可以把PhalApi的phalapi-buildtest脚本命令整合到ThinkPHP 5 的项目里,
非常重要的链接
使用说明和详细的官方文档:PhalApi 2.x 单元测试
脚本命令Github下载地址(需要同时下载这两个文件):
https://github.com/phalapi/phalapi/blob/master-2x/bin/phalapi-buildtest
https://github.com/phalapi/phalapi/blob/master-2x/bin/build_test.php
下载后放到tests/phpunit目录下,此时文件结构如下:
$ tree ./phpunit
./phpunit
├── bootstrap.php
├── build_test.php
├── demo
│ └── TestCaseClass.php
├── phalapi-buildtest
└── phpunit.xml
准备好后,就可以开始生成单元测试的代码啦!
例如,可执行:
$ ./phalapi-buildtest ../../application/controller/Site.php 'app\controller\Site' > ./controller/SiteTest.php
生成后,便可执行。
难点3:如何测试controller,以及如何解决input()的参数缓存?
正常情况下,进行单个单元测试时,以下测试代码是可以的:
public function testLogin()
{
$_POST['email'] = 'phpunit123';
$_POST['password'] = '123456';
$_POST['remember'] = '1';
$rs = $this->appcontrollerSite->login();
$this->assertEquals(1, $rs['code']);
}
对应的源代码是:
<?php
class Site extends Controller {
public function login() {
$username = input('post.email');
$password = input('post.password');
$remember = input('post.remember/d', 0);
// todo
}
}
但是,如果执行全部单元测试的话,传给controller的$_POST参数就失效了。这是因为ThinkPHP5的Request是一个单例,并且在think\Request::$post变量中缓存了POST参数,导致后面的参数不生效。
为此,需要这样调整传递参数:
public function testLogin()
{
$_POST['email'] = 'phpunit123';
$_POST['password'] = '123456';
$_POST['remember'] = '1';
// 加多这两行,重置POST参数
$params = ['POST' => $_POST];
\think\Request::create('/', 'POST', $params);
$rs = $this->appcontrollerSite->login();
$this->assertEquals(1, $rs['code']);
}
参考
发现了一篇写得很赞的文章:PHPUnit简介及使用(thinkphp5的单元测试安装及使用)
来源:oschina
链接:https://my.oschina.net/u/256338/blog/3047041