PHP单元测试框架PHPUnit用法详解


Posted in PHP onJanuary 23, 2019

本文实例讲述了PHP单元测试框架PHPUnit用法。分享给大家供大家参考,具体如下:

以前在学习IOS开发时有专门写过Objective-C的单元测试的文章,IOS开发学习之单元测试,今天再总结下怎么在PHP中使用单元测试。

一、前言

在这篇文章中,我们使用 composer 的依赖包管理工具进行phpunit包安装和管理,composer 官方地址 https://getcomposer.org/,按照提示进行全局安装即可,另外,我们也会使用一个非常好用的Monolog记录日志组件记录日志,方便我们查看。

在根目录下建立 coomposer.json 的配置文件,输入以下内容:

{
  "autoload": {
    "classmap": [
      "./"
    ]
  }
}

上面的意思是将根目录下的所有的类文件都加载进来, 在命令行执行 composer install 后,在根目录会生成出一个vendor的文件夹,我们以后通过 composer 安装的任何第三方代码都会被生成在这里。

二、为什么要单元测试?

只要你想到输入一些东西到print语句或调试表达式中,就用测试代替它。 --Martin Fowler

PHPUnit 是一个用PHP编程语言开发的开源软件,是一个单元测试框架。PHPUnit由Sebastian Bergmann创建,源于Kent Beck的SUnit,是xUnit家族的框架之一。

单元测试是对单独的代码对象进行测试的过程,比如对函数、类、方法进行测试。单元测试可以使用任意一段已经写好的测试代码,也可以使用一些已经存在的测试框架,比如JUnit、PHPUnit或者Cantata++,单元测试框架提供了一系列共同、有用的功能来帮助人们编写自动化的检测单元,例如检查一个实际的值是否符合我们期望的值的断言。单元测试框架经常会包含每个测试的报告,以及给出你已经覆盖到的代码覆盖率。

总之一句话,使用 phpunit 进行自动测试,会使你的代码更健壮,减少后期维护的成本,也是一种比较标准的规范,现如今流行的PHP框架都带了单元测试,如Laraval,Symfony,Yii2等,单元测试已经成了标配。

另外,单元测试用例是通过命令操控测试脚本的,而不是通过浏览器访问URL的。

三、安装PHPUnit

使用 composer 方式安装 PHPUnit,其他安装方式请看这里

composer require --dev phpunit/phpunit ^6.2

安装 Monolog 日志包,做 phpunit 测试记录日志用。

composer require monolog/monolog

安装好之后,我们可以看coomposer.json 文件已经有这两个扩展包了:

"require": {
  "monolog/monolog": "^1.23",
  },
"require-dev": {
    "phpunit/phpunit": "^6.2"
  },

四、PHPUnit简单用法

1、单个文件测试

创建目录tests,新建文件 StackTest.php,编辑如下:

<?php
/**
 * 1、composer 安装Monolog日志扩展,安装phpunit单元测试扩展包
 * 2、引入autoload.php文件
 * 3、测试案例
 *
 *
 */
namespace App\tests;
require_once __DIR__ . '/../vendor/autoload.php';
define("ROOT_PATH", dirname(__DIR__) . "/");
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use PHPUnit\Framework\TestCase;
class StackTest extends TestCase
{
  public function testPushAndPop()
  {
    $stack = [];
    $this->assertEquals(0, count($stack));
    array_push($stack, 'foo');
    // 添加日志文件,如果没有安装monolog,则有关monolog的代码都可以注释掉
    $this->Log()->error('hello', $stack);
    $this->assertEquals('foo', $stack[count($stack)-1]);
    $this->assertEquals(1, count($stack));
    $this->assertEquals('foo', array_pop($stack));
    $this->assertEquals(0, count($stack));
  }
  public function Log()
  {
    // create a log channel
    $log = new Logger('Tester');
    $log->pushHandler(new StreamHandler(ROOT_PATH . 'storage/logs/app.log', Logger::WARNING));
    $log->error("Error");
    return $log;
  }
}

代码解释:

StackTest为测试类

StackTest 继承于 PHPUnit\Framework\TestCase

测试方法testPushAndPop(),测试方法必须为public权限,一般以test开头,或者你也可以选择给其加注释@test来表

在测试方法内,类似于 assertEquals() 这样的断言方法用来对实际值与预期值的匹配做出断言。

命令行执行:

phpunit 命令 测试文件命名

➜ framework# ./vendor/bin/phpunit tests/StackTest.php
// 或者可以省略文件后缀名
// ./vendor/bin/phpunit tests/StackTest

执行结果:

➜  framework# ./vendor/bin/phpunit tests/StackTest.php
PHPUnit 6.4.1 by Sebastian Bergmann and contributors.
.                                                                   1 / 1 (100%)
Time: 56 ms, Memory: 4.00MB
OK (1 test, 5 assertions)

我们可以在app.log文件中查看我们打印的日志信息。

2、类文件引入

Calculator.php

<?php
class Calculator
{
  public function sum($a, $b)
  {
    return $a + $b;
  }
}
?>

单元测试类:

CalculatorTest.php

<?php
namespace App\tests;
require_once __DIR__ . '/../vendor/autoload.php';
require "Calculator.php";
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
  public function testSum()
  {
    $obj = new Calculator;
    $this->assertEquals(0, $obj->sum(0, 0));
  }
}

命令执行:

> ./vendor/bin/phpunit tests/CalculatorTest

执行结果:

PHPUnit 6.4.1 by Sebastian Bergmann and contributors.
F                                                                   1 / 1 (100%)
Time: 117 ms, Memory: 4.00MB
There was 1 failure:

如果我们把这里的断言故意写错,$this->assertEquals(1, $obj->sum(0, 0));

看执行结果:

PHPUnit 6.4.1 by Sebastian Bergmann and contributors.
F                                                                   1 / 1 (100%)
Time: 117 ms, Memory: 4.00MB
There was 1 failure:
1) App\tests\CalculatorTest::testSum
Failed asserting that 0 matches expected 1.
/Applications/XAMPP/xamppfiles/htdocs/web/framework/tests/CalculatorTest.php:22
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

会直接报出方法错误信息及行号,有助于我们快速找出bug

3、高级用法

你是否已经厌烦了在每一个测试方法命名前面加一个test,是否因为只是调用的参数不同,却要写多个测试用例而纠结?我最喜欢的高级功能,现在隆重推荐给你,叫做框架生成器。

Calculator.php

<?php
class Calculator
{
  public function sum($a, $b)
  {
    return $a + $b;
  }
}
?>

命令行启动测试用例,使用关键字 --skeleton

> ./vendor/bin/phpunit --skeleton Calculator.php

执行结果:

PHPUnit 6.4.1 by Sebastian Bergmann and contributors.
Wrote test class skeleton for Calculator to CalculatorTest.php.

是不是很简单,因为没有测试数据,所以这里加测试数据,然后重新执行上边的命令

<?php
class Calculator
{
  /**
   * @assert (0, 0) == 0
   * @assert (0, 1) == 1
   * @assert (1, 0) == 1
   * @assert (1, 1) == 2
   */
  public function sum($a, $b)
  {
    return $a + $b;
  }
}
?>

原始类中的每个方法都进行@assert注解的检测。这些被转变为测试代码,像这样

/**
 * Generated from @assert (0, 0) == 0.
 */
public function testSum() {
  $obj = new Calculator;
  $this->assertEquals(0, $obj->sum(0, 0));
}

执行结果:

./vendor/bin/phpunit tests/CalculatorTest
PHPUnit 6.4.1 by Sebastian Bergmann and contributors.
....
Time: 0 seconds
OK (4 tests)

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP 翻页 实例代码
Aug 07 PHP
PHP学习笔记之数组篇
Jun 28 PHP
Apache服务器无法使用的解决方法
May 08 PHP
php之curl设置超时实例
Nov 03 PHP
PHP模块memcached使用指南
Dec 08 PHP
PHP常见数组函数用法小结
Mar 21 PHP
今天你说520了吗?不仅有php表白书还有java表白神器
May 20 PHP
浅谈htmlentities 、htmlspecialchars、addslashes的使用方法
Dec 09 PHP
PHP preg_match实现正则表达式匹配功能【输出是否匹配及匹配值】
Jul 19 PHP
PHP弱类型语言中类型判断操作实例详解
Aug 10 PHP
PHP代码重构方法漫谈
Apr 17 PHP
laravel按天、按小时,查询数据的实例
Oct 09 PHP
PHPStudy下如何为Apache安装SSL证书的方法步骤
Jan 23 #PHP
PHP实现浏览器格式化显示XML的方法示例
Jan 22 #PHP
php语法检查的方法总结
Jan 21 #PHP
PHP删除字符串中非字母数字字符方法总结
Jan 20 #PHP
PHP Laravel中的Trait使用方法
Jan 20 #PHP
PHP合并两个或多个数组的方法
Jan 20 #PHP
实例讲解通过​PHP创建数据库
Jan 20 #PHP
You might like
使用 MySQL 开始 PHP 会话
2006/12/21 PHP
PHP生成UTF8文件的方法
2010/05/15 PHP
用PHP将数据导入到Foxmail的实现代码
2010/09/05 PHP
array_multisort实现PHP多维数组排序示例讲解
2011/01/04 PHP
PHP判断远程图片或文件是否存在的实现代码
2014/02/20 PHP
php版微信自定义回复功能示例
2016/12/05 PHP
javascript中对对层的控制
2006/12/29 Javascript
新手入门常用代码集锦
2007/01/11 Javascript
JavaScript中Object和Function的关系小结
2009/09/26 Javascript
用JavaScript实现动画效果的方法
2013/07/20 Javascript
JS控制图片等比例缩放的示例代码
2013/12/24 Javascript
jQuery插件animateSlide制作多点滑动幻灯片
2015/06/11 Javascript
使用Meteor配合Node.js编写实时聊天应用的范例
2015/06/23 Javascript
jQuery实现标签页效果实战(4)
2017/02/08 Javascript
JS 中LocalStorage和SessionStorage的使用
2017/08/17 Javascript
vue组件详解之使用slot分发内容
2018/04/09 Javascript
React router动态加载组件之适配器模式的应用详解
2018/09/12 Javascript
解决angularjs WdatePicker ng-model的问题
2018/09/13 Javascript
JS实现的合并两个有序链表算法示例
2019/02/25 Javascript
js实现简单贪吃蛇游戏
2020/05/15 Javascript
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
详解Python3中yield生成器的用法
2015/08/20 Python
Python自定义主从分布式架构实例分析
2016/09/19 Python
书单|人生苦短,你还不用python!
2017/12/29 Python
浅谈DataFrame和SparkSql取值误区
2018/06/09 Python
Python骚操作之动态定义函数
2019/03/26 Python
python 比较2张图片的相似度的方法示例
2019/12/18 Python
详解Python中import机制
2020/09/11 Python
CSS3的resize属性使用初探
2015/09/27 HTML / CSS
Johnston & Murphy官网: 约翰斯顿·墨菲牛津总统鞋
2018/01/09 全球购物
2014年乡镇植树节活动方案
2014/02/28 职场文书
广告传媒专业应届生求职信
2014/03/01 职场文书
大学四年个人总结
2015/03/03 职场文书
《植树问题》教学反思
2016/03/03 职场文书
redis使用不当导致应用卡死bug的过程解析
2021/07/01 Redis
Win11 25163.1010更新补丁KB5016904推送,测试服务验证管道(附更新修复汇总)
2022/07/23 数码科技