composer安装和phpunit单元测试
工要善其事,必先利其器,首先我们准备的是强大开发工具IDE : PhpStorm
我们用的是最新版 phpstorm 2019.3 (网上有破解激活方法)
安装环境:composer
官网安装教程: https://getcomposer.org/download/
windows安装composer:
最简单的方法,新建文件composer-setup.bat 编辑器打开 将下面的代码拷贝进去,双击运行,完成后目录下就会下载一个文件composer.phar
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" php composer-setup.php php -r "unlink('composer-setup.php');"
用 php composer.phar 命令就可以开启使用 composer之旅啦:
php composer.phar --version
当然每次敲 php composer.phar 代码太麻烦了,有没有办法省略为 直接composer命令,可以这样做
1. 把当前目录 E:\PHPServer\phplibrary 添加到系统环境变量$PATH中:
点击编辑,弹出窗口:
在当前目录 E:\PHPServer\phplibrary 下 新建一个 composer.bat 文件,将下面代码拷贝进去保存即可。以后就可以直接使用composer命令了。
@php "%~dp0composer.phar" %*
省去输入.bat的做法,在当前目录 E:\PHPServer\phplibrary 下 新建一个 composer 文件(无后缀名),将下面代码拷贝进去保存即可。
#!/usr/bin/env sh # php /path/to/composer.phar $* php `dirname $0`/composer.phar $*
PHP Composer各大厂商镜像全镜像地址,如果使用composer安装过程过于缓慢,可以切换为国内镜像:
https://www.cnblogs.com/phplog/articles/11297728.html
安装phpuinit:
官网下载安装教程: https://phpunit.de/getting-started/phpunit-8.html
几个版本的包下载地址(官网)
https://phar.phpunit.de/phpunit-8.phar
https://phar.phpunit.de/phpunit-7.phar
https://phar.phpunit.de/phpunit-6.phar
https://phar.phpunit.de/phpunit-5.phar
备用下载地址:
http://phar.phpunit.cn/phpunit-4.8.phar
http://phar.phpunit.cn/phpunit-7.0.phar
http://phar.phpunit.cn/phpunit-8.0.phar
http://phar.phpunit.cn/phpunit.phar (最新版的包)
linux安装:
wget http://phar.phpunit.cn/phpunit-7.0.phar chmod +x phpunit-7.0.phar mv phpunit-7.0.phar /usr/local/bin/phpunit phpunit --version
windows安装:
cd E:\PHPServer\phplibrary curl -O http://phar.phpunit.cn/phpunit.phar # phpunit --version
在当前目录 E:\PHPServer\phplibrary 下 新建一个 phpunit.bat 文件,将下面代码拷贝进去保存即可。以后就可以直接使用phpunit命令了。
@php "%~dp0phpunit.phar" %*
省去输入.bat的做法,在当前目录 E:\PHPServer\phplibrary 下 新建一个 phpunit 文件(无后缀名),将下面代码拷贝进去保存即可。
#!/usr/bin/env sh
# php /path/to/phpunit.phar $*
php `dirname $0`/phpunit.phar $*
phpunit各个版本支持情况,可以查阅网址: http://www.phpunit.cn
主版本 | 初始版本 | PHP兼容性 | 支持 |
PHPUnit 8 | 2019年2月1日 | PHP 7.2, PHP 7.3, PHP 7.4 | 在2021年2月5日结束支持 |
PHPUnit 7 | 2018年2月2日 | PHP 7.1, PHP 7.2, PHP 7.3 | 在2020年2月7日结束支持 |
PHPUnit 6 | 2017年2月3日 | PHP 7.0, PHP 7.1, PHP 7.2 | 在2019年2月1日结束支持 |
PHPUnit 5 | 2015年10月2日 | PHP 5.6, PHP 7.0, PHP 7.1 | 在2018年2月2日结束支持 |
PHPUnit 4 | 2014年3月7日 | PHP 5.3, PHP 5.4, PHP 5.5, PHP 5.6 | 在2017年2月3日结束支持 |
PHPUnit 8于2019年2月1日发布。它将支持PHP 7.2, PHP 7.3, PHP 7.4.
使用 PhpStorm 调试 PHPUnit 单元测试:
首先展示下我的运行效果:
首先 配置 php 运行版本(我在系统环境变量$PATH添加的是 E:\PHPServer\phpStudy_v8\Extensions\php\php7.2.9nts (php7.2版本),所以IDE也要改成一致) :
Languages & Frameworks > PHP > Test Frameworks
点击 + 新增一个 PHPUnit Local
执行单元测试
方式1: Phpstorm方式,当前测试类右键Run即可
方式2:命令行方式,进入项目目录执行
要引入命名空间文件,让其能自动加载,我们可以编写一个autoload.php , 或 Bootstrap.php ,当然文件名可以随意。
<?php
# Bootstrap.php 文件 function autoloader($dir) { spl_autoload_register(function ($class) use ($dir) { if (class_exists($class)) { return true; } $pathPsr4 = $dir."/".strtr($class, '\\', DIRECTORY_SEPARATOR) . ".php"; if (file_exists($pathPsr4)){ include_once $pathPsr4; } return true; }); } define('BOOT_ROOT', __DIR__); autoloader(BOOT_ROOT);
创建一个单元测试文件 EmailTest.php:
<?php # EmailTest.php 文件 namespace tests; require_once __DIR__ . '/../Bootstrap.php'; use PHPUnit\Framework\TestCase; use src\Email; final class EmailTest extends TestCase { public function __construct($name = null, array $data = [], $dataName = '') { parent::__construct($name, $data, $dataName); } public function testCanBeCreatedFromValidEmailAddress(): void { $this->assertInstanceOf( Email::class, Email::fromString('user@example.com') ); } public function testCannotBeCreatedFromInvalidEmailAddress(): void { $this->expectException(\InvalidArgumentException::class); Email::fromString('invalid'); } public function testCanBeUsedAsString(): void { $this->assertEquals( 'user2@example.com', Email::fromString('user@example.com') ); } }
里面的代码 引入了autoload 的 Bootstrap文件: require_once __DIR__ . '/../Bootstrap.php';
所以phpunit可以不指定 bootstrap选项来运行:
phpunit tests/EmailTest.php
如果没有这句文件引用进来,就得手动指定 autoload配置了:
# 指定autoload配置: phpunit --bootstrap Bootstrap.php tests/EmailTest.php
用 PhpStorm 进行单元测试:
手动指定 bootstrap file :
点击选择文件,右键菜单Debug
或者点击工具栏上的运行按钮:
执行结果:
phpstorm就会自动加上phpunit运行参数来运行测试 phpunit,例如:
E:\PHPServer\phpStudy_v8\Extensions\php\php7.2.9nts\php.exe -dxdebug.remote_enable=1 -dxdebug.remote_mode=req -dxdebug.remote_port=9100 -dxdebug.remote_host=127.0.0.1 E:\PHPServer\phplibrary\phpunit.phar --bootstrap E:\Develop-Work\my-project-list\my-work-list\phpTest\Bootstrap.php --no-configuration tests\EmailTest2 E:\Develop-Work\my-project-list\my-work-list\phpTest\tests\EmailTest2.php --teamcity
另外,配置 phpunit.xml (可选),例如:
安装 thinkphp 5.1 :
文档: https://www.kancloud.cn/manual/thinkphp5_1/353948
composer命令执行安装:
composer create-project topthink/think=5.1.* thinkapp
执行完成:
将生成一个目录 thinkapp
编辑 public/index.php, 原代码:
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- // [ 应用入口文件 ] namespace think; // 加载基础文件 require __DIR__ . '/../thinkphp/base.php'; // 支持事先使用静态方法设置Request对象和Config对象 // 执行应用并响应 Container::get('app')->run()->send();
比如我们开发项目可能加个常量等,修改 public/index.php文件:
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- // [ 应用入口文件 ] namespace think; define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); define('APP_PATH', ROOT_PATH . 'application' . DIRECTORY_SEPARATOR); define('ADDON_PATH', ROOT_PATH . 'addons' . DIRECTORY_SEPARATOR); // 加载基础文件 require ROOT_PATH . 'thinkphp' . DIRECTORY_SEPARATOR . 'base.php'; // 支持事先使用静态方法设置Request对象和Config对象 // 执行应用并响应 Container::get('app')->run()->send();
修改 console控制台文件: think
原代码:
#!/usr/bin/env php <?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: yunwuxin <448901948@qq.com> // +---------------------------------------------------------------------- namespace think; // 加载基础文件 require __DIR__ . '/thinkphp/base.php'; // 应用初始化 Container::get('app')->path(__DIR__ . '/application/')->initialize(); // 控制台初始化 Console::init();
修改 think 文件代码:
#!/usr/bin/env php <?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: yunwuxin <448901948@qq.com> // +---------------------------------------------------------------------- namespace think; define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR); define('APP_PATH', ROOT_PATH . 'application' . DIRECTORY_SEPARATOR); define('ADDON_PATH', ROOT_PATH . 'addons' . DIRECTORY_SEPARATOR); // 加载基础文件 require ROOT_PATH . '/thinkphp/base.php'; // 应用初始化 Container::get('app')->path(__DIR__ . '/application/')->initialize(); // 控制台初始化 Console::init();
创建和配置网站:
设置伪静态:
if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=/$1 last; break; }
最后浏览器访问:
查看thinkphp 5.1的版本:
打开 thinkphp/library/think/App.php 可以看到版本号
或者通过命令查看 :
php think version
配置 phpstorm :
安装thinkphp 单元测试:
文档地址: https://www.kancloud.cn/manual/thinkphp5_1/354125
执行composer 命令进行安装:
composer require topthink/think-testing=2.0.*
成功安装后,根目录就会多出 tests文件夹和 phpunit.xml 文件
如果没有生成这两个的文件夹和文件,可以到 https://github.com/top-think/think-testing 下载示例。
执行 composer update 更新包到最新:
composer update
执行 命令 php think unit 进行单元测试:
在 tests 文件夹 ,右键, New > PHP Test > PHPUint Test 创建一个新的单元测试文件 NewTest.php :
生成的 NewTest.php 文件:
新生成的 tests\NewTest.php 原代码:
<?php use PHPUnit\Framework\TestCase; class NewTest extends TestCase { }
编辑 tests\NewTest.php文件:
<?php # tests/NewTest.php 文件 # 注意: NewTest 是继承 PHPUnit\Framework\TestCase, 不是继承 \think\testing\TestCase # 所以, php think unit 也只能测试 普通的 单元测试方法, 像 $this->visit('/index/test/show')->see('show_test'); 这种就会提示找不到 visit方法 # 要想能支持 visit, see方法, 只能改继承父类方法: class NewTest extends \think\testing\TestCase use PHPUnit\Framework\TestCase; class NewTest extends TestCase { #protected $baseUrl = 'http://www.thinkapp.com'; public function testBasicFunctions() { $this->assertTrue(true); $this->assertEquals(2, 1 + 1); $app = new \app\index\controller\Test(); // 假设 index/test/show 方法返回的字符串中包含 "show_test" $this->assertContains('show_test', $app->show()); } public function testTest() { $stack = []; $this->assertEquals(0, count($stack)); } public function testSomethingIsTrue() { $this->assertTrue(true); } public function testSum() { $obj = new \app\index\controller\Test(); $this->assertEquals(6, $obj->testNum(2, 3)); } // 测试出错代码 public function testError() { $obj = new \app\index\controller\Test(); $this->assertEquals(6, $obj->testError(2, 3)); } }
在 application/index/controller 目录下 创建一个 Test.php 文件
<?php # application/index/controller/Test.php 文件 namespace app\index\controller; class Test { public function testNum($a, $b) { $c = $a * $b; return $c; } public function testError($a, $b) { $c = $a * $b; return $z; // 故意写错成 $z } public function show() { return 'show_test'; } }
命令行再次执行 php think unit 进行单元测试:
另外thinkphp自带的测试单元方法:
在 tests 文件夹下 新建 ThinkTest.php (这个就支持的比较全面)
<?php # tests/ThinkTest.php 文件 # 注意: ThinkTest 是继承 \think\testing\TestCase, 不是继承 \PHPUnit\Framework\TestCase # 所以, php think unit 能支持 thinkphp 自身测试单元的 visit, see方法, 例如 $this->visit('/index/test/show')->see('show_test'); # 所有的 PHPUnit 的方法都支持。 namespace tests; class ThinkTest extends \think\testing\TestCase { protected $baseUrl = 'http://www.thinkapp.com'; public function testBasicFunctions() { $app = new \app\index\controller\Test(); // 假设 index/test/show 方法返回的字符串中包含 "show_test" $this->assertContains('show_test', $app->show()); } public function testBasicExample() { $this->visit('/index/test/show')->see('show_test'); } }
执行 php think unit 命令后的效果:
可单独运行phpunit命令的单元测试文件,
方法:例如在tests文件夹下 新建 UnitTest.php 文件:
<?php # tests/UnitTest.php 文件 # 注意: UnitTest 是继承 \PHPUnit\Framework\TestCase # UnitTest 可以单独执行 phpunit命令测试 thinkphp 框架项目。 # 执行命令: phpunit tests/UnitTest.php namespace tests; use PHPUnit\Framework\TestCase; class UnitTest extends TestCase { public function __construct($name = null, array $data = [], $dataName = '') { # 几个常量定义,摘抄自 public/index.php 里的文件定义 defined('ROOT_PATH') or define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); defined('APP_PATH') or define('APP_PATH', ROOT_PATH . 'application' . DIRECTORY_SEPARATOR); defined('ADDON_PATH') or define('ADDON_PATH', ROOT_PATH . 'addons' . DIRECTORY_SEPARATOR); // 引入需要的环境 require_once ROOT_PATH . '/thinkphp/base.php'; // 初始化 App 对象,并将 APP_PATH 指向项目的 application 目录 //\think\App::getInstance()->path(__DIR__ . '/../application/')->initialize(); \think\Container::get('app')->path(__DIR__ . '/../application/')->initialize(); parent::__construct($name, $data, $dataName); } public function testSomethingIsTrue() { $this->assertTrue(false); } public function testSum() { $obj = new \app\index\controller\Test(); $this->assertEquals(6, $obj->testNum(2, 3)); } public function testError() { $obj = new \app\index\controller\Test(); $this->assertEquals(6, $obj->testError(2, 3)); } }
执行 phpunit 命令进行单元测试:
phpunit tests/UnitTest.php
配置编辑器:
在 文件 UnitTest.php 右键 > Debug (PHPUnit)
更改默认的cmd客户端,比如更改成 Git Bash (路径:D:\Program Files\Git\bin\sh.exe)
修改: Settings > Tools > Terminal > Application settings > Shell path, 输入 D:\Program Files\Git\bin\sh.exe
推荐更强大的单元测试工具:codeception单元测试
请查看我的另一篇文章介绍:
phpstorm集成codeception单元测试 ( https://www.cnblogs.com/phplog/articles/12058369.html )
tp 3.2 的单元测试可以参考(没测试过,请自行测试):
https://www.kancloud.cn/code7/tpunit/378491
https://github.com/CODE7070/TPUNIT
phpunit assert断言分类整理
布尔类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertTrue | 断言为真 | ||
assertFalse | 断言为假 |
NULL类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertNull | 断言为NULL | ||
assertNotNull | 断言非NULL |
数字类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertEquals | 断言等于 | ||
assertNotEquals | 断言不等于 | ||
assertGreaterThan | 断言大于 | ||
assertGreaterThanOrEqual | 断言大于等于 | ||
assertLessThan | 断言小于 | ||
assertLessThanOrEqual | 断言小于等于 |
字符类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertEquals | 断言等于 | ||
assertNotEquals | 断言不等于 | ||
assertContains | 断言包含 | ||
assertNotContains | 断言不包含 | ||
assertContainsOnly | 断言只包含 | ||
assertLessThanOrEqual | 断言小于等于 | ||
assertNotContainsOnly | 断言不只包含 |
数组类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertEquals | 断言等于 | ||
assertNotEquals | 断言不等于 | ||
assertArrayHasKey | 断言有键 | ||
assertArrayNotHasKey | 断言没有键 | ||
assertContains | 断言包含 | ||
assertNotContains | 断言不包含 | ||
assertContainsOnly | 断言只包含 | ||
assertNotContainsOnly | 断言不只包含 |
对象类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertAttributeContains | 断言属性包含 | ||
assertAttributeContainsOnly | 断言属性只包含 | ||
assertAttributeEquals | 断言属性等于 | ||
assertAttributeGreaterThan | 断言属性大于 | ||
assertAttributeGreaterThanOrEqual | 断言属性大于等于 | ||
assertAttributeLessThan | 断言属性小于 | ||
assertAttributeLessThanOrEqual | 断言属性小于等于 | ||
assertAttributeNotContains | 断言不包含 | ||
assertAttributeNotContainsOnly | 断言属性不只包含 | ||
assertAttributeNotEquals | 断言属性不等于 | ||
assertAttributeNotSame | 断言属性不相同 | ||
assertAttributeSame | 断言属性相同 | ||
assertSame | 断言类型和值都相同 | ||
assertNotSame | 断言类型或值不相同 | ||
assertObjectHasAttribute | 断言对象有某属性 | ||
assertObjectNotHasAttribute | 断言对象没有某属性 |
class类型
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertClassHasAttribute | 断言类有某属性 | ||
assertClassHasStaticAttribute | 断言类有某静态属性 | ||
assertClassNotHasAttribute | 断言类没有某属性 | ||
assertClassNotHasStaticAttribute | 断言类没有某静态属性 |
文件相关
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertFileEquals | 断言文件内容等于 | ||
assertFileExists | 断言文件存在 | ||
assertFileNotEquals | 断言文件内容不等于 | ||
assertFileNotExists | 断言文件不存在 |
XML相关
方法名 | 含义 | 参数 | 返回值 |
---|---|---|---|
assertXmlFileEqualsXmlFile | 断言XML文件内容相等 | ||
assertXmlFileNotEqualsXmlFile | 断言XML文件内容不相等 | ||
assertXmlStringEqualsXmlFile | 断言XML字符串等于XML文件内容 | ||
assertXmlStringEqualsXmlString | 断言XML字符串相等 | ||
assertXmlStringNotEqualsXmlFile | 断言XML字符串不等于XML文件内容 | ||
assertXmlStringNotEqualsXmlString | 断言XML字符串不相等 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
phpunit中相关的几个断言:
assertTrue / assertFalse 断言是否为真值还是假
assertEquals 判断输出是否和预期的相等
assertGreaterThan 断言结果是否大于某个值,同样的也有lessThan(小于), greaterThanOrEqual(大于等于),
lessThanOrEqual (小于等于).
assertContains 判断输入是否包含指定的值
assertType 判断是否属于指定类型
assertNull 判断是否为空值
assertFileExists 判断文件是否存在
assertRegExp 根据正则表达式判断
其他参数资料:
上面单元测试实例源码已经放到Github上了: https://github.com/lajox/thinkphp-codeception-example
关于断言: https://phpunit.readthedocs.io/zh_CN/latest/
PHPUnit文档: https://phpunit.de/manual/6.5/en/writing-tests-for-phpunit.html
中文文档: http://www.phpunit.cn/getting-started.html
中文文档:http://www.phpunit.cn/manual/current/zh_cn/installation.html
https://phpunit.readthedocs.io/zh_CN/latest/index.html
https://www.cnblogs.com/martini-d/p/phpstorm-pei-zhiphpunit.html#autoid-4-1-0