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 相关文章推荐
第4章 数据处理-php数组的处理-郑阿奇
Jul 04 PHP
修改php.ini以达到屏蔽错误信息并记录日志
Jun 16 PHP
phpmyadmin config.inc.php配置示例
Aug 27 PHP
PHP将字符分解为多个字符串的方法
Nov 22 PHP
纯php生成随机密码
Oct 30 PHP
PHP读取文件内容的五种方式
Dec 28 PHP
WordPress分页伪静态加html后缀
Jun 08 PHP
PHP中单例模式与工厂模式详解
Feb 17 PHP
PHP基于自定义函数生成笛卡尔积的方法示例
Sep 30 PHP
PHP学习笔记之session
May 06 PHP
PHP Primary script unknown 解决方法总结
Aug 22 PHP
thinkphp诸多限制条件下如何getshell详解
Dec 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
在php和MySql中计算时间差的方法
2011/04/22 PHP
php实现单链表的实例代码
2013/03/22 PHP
php不写闭合标签的好处
2014/03/04 PHP
ThinkPHP3.2.2实现持久登录(记住我)功能的方法
2016/05/16 PHP
javascript html 静态页面传参数
2009/04/10 Javascript
JavaScript中判断函数是new还是()调用的区别说明
2011/04/07 Javascript
JQuery插件jcarousellite的参数中文说明
2015/05/11 Javascript
js实现上一页下一页的效果【附代码】
2016/03/10 Javascript
Google 地图叠加层实例讲解
2016/08/06 Javascript
微信小程序实现图片轮播及文件上传
2017/04/07 Javascript
利用JS如何计算字符串所占字节数示例代码
2017/09/13 Javascript
浅谈vue2 单页面如何设置网页title
2017/11/08 Javascript
解决一个微信号同时支持多个环境网页授权问题
2019/08/07 Javascript
js+css实现全屏侧边栏
2020/06/16 Javascript
vue移动端写的拖拽功能示例代码
2020/09/09 Javascript
OpenLayers3实现对地图的基本操作
2020/09/28 Javascript
python刷投票的脚本实现代码
2014/11/08 Python
浅谈DataFrame和SparkSql取值误区
2018/06/09 Python
Python使用crontab模块设置和清除定时任务操作详解
2019/04/09 Python
python 获取sqlite3数据库的表名和表字段名的实例
2019/07/17 Python
Django分页功能的实现代码详解
2019/07/29 Python
python cumsum函数的具体使用
2019/07/29 Python
python实现代码统计程序
2019/09/19 Python
Python求解正态分布置信区间教程
2019/11/20 Python
Python rabbitMQ如何实现生产消费者模式
2020/08/24 Python
pandas map(),apply(),applymap()区别解析
2021/02/24 Python
HTML5中canvas中的beginPath()和closePath()的重要性
2018/08/24 HTML / CSS
文明礼仪事迹材料
2014/01/09 职场文书
房地产销售计划书
2014/01/10 职场文书
幼儿园义卖活动方案
2014/01/17 职场文书
城市轨道交通工程职业规划书范文
2014/01/18 职场文书
党务公开方案
2014/05/06 职场文书
护士长2014年终工作总结
2014/11/11 职场文书
校运会宣传稿大全
2015/07/23 职场文书
个人工作总结怎么写?
2019/04/09 职场文书
详解vue中v-for的key唯一性
2021/05/15 Vue.js