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模板引擎SMARTY
Oct 09 PHP
深入了解php4(2)--重访过去
Oct 09 PHP
木翼下载系统中说明的PHP安全配置方法
Jun 16 PHP
PHP爆绝对路径方法收集整理
Sep 17 PHP
关于PHP二进制流 逐bit的低位在前算法(详解)
Jun 13 PHP
PHP四种基本排序算法示例
Apr 09 PHP
thinkPHP中create方法与令牌验证实例浅析
Dec 08 PHP
Laravel重写用户登录简单示例
Oct 08 PHP
PHP基于imagick扩展实现合成图片的两种方法【附imagick扩展下载】
Nov 14 PHP
PHP递归实现快速排序的方法示例
Dec 18 PHP
laravel配置Redis多个库的实现方法
Apr 10 PHP
PHP从零开始打造自己的MVC框架之类的自动加载实现方法详解
Jun 03 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实现两个数组相加的方法
2015/02/17 PHP
FCK调用方法..
2006/12/21 Javascript
JSON扫盲帖 JSON.as类教程
2009/02/16 Javascript
javascript 有用的脚本函数
2009/05/07 Javascript
javascript 去字符串空格终极版(支持utf8)
2009/11/14 Javascript
jQuery.Validate 使用笔记(jQuery Validation范例 )
2010/06/25 Javascript
jquery-easyui关闭tab自动切换到前一个tab
2010/07/29 Javascript
jQuery 表单验证扩展(四)
2010/10/20 Javascript
js TextArea的选中区域处理
2010/12/28 Javascript
js实现可拖动DIV的方法
2013/12/17 Javascript
JS+CSS实现简单滑动门(滑动菜单)效果
2015/09/19 Javascript
JS实现带圆弧背景渐变效果的导航菜单代码
2015/10/13 Javascript
JS中JSON对象和String之间的互转及处理技巧
2016/04/06 Javascript
微信小程序  生命周期详解
2016/10/27 Javascript
Angular中ng-repeat与ul li的多层嵌套重复问题
2017/07/24 Javascript
解决JS表单验证只有第一个IF起作用的问题
2018/12/04 Javascript
在SSM框架下用laypage和ajax实现分页和数据交互的方法
2019/09/27 Javascript
[01:00]DOTA2 store: Collection of Artisan's Wonders
2015/08/12 DOTA
python中的实例方法、静态方法、类方法、类变量和实例变量浅析
2014/04/26 Python
Python Web框架Flask中使用百度云存储BCS实例
2015/02/08 Python
浅谈python正则的常用方法 覆盖范围70%以上
2018/03/14 Python
关于python2 csv写入空白行的问题
2018/06/22 Python
浅析python继承与多重继承
2018/09/13 Python
Python pandas RFM模型应用实例详解
2019/11/20 Python
Ubuntu中配置TensorFlow使用环境的方法
2020/04/21 Python
Currentbody德国站:健康与美容技术专家
2020/04/05 全球购物
广州御银科技股份有限公司试卷(C++)
2016/11/04 面试题
酒店保洁主管岗位职责
2013/11/28 职场文书
加油口号大全
2014/06/13 职场文书
销售简历自我评价怎么写
2014/09/26 职场文书
老兵退伍标语
2014/10/07 职场文书
股权转让协议范本
2014/12/07 职场文书
我们的节日重阳节活动总结
2015/03/24 职场文书
社会实践心得体会范文
2016/01/14 职场文书
浅谈Mysql多表连接查询的执行细节
2021/04/24 MySQL
使用 Koa + TS + ESLlint 搭建node服务器的过程详解
2022/05/30 NodeJs