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截取中文字符串的问题
Jul 12 PHP
第十四节--命名空间
Nov 16 PHP
用php实现让页面只能被百度gogole蜘蛛访问的方法
Dec 29 PHP
php 上传文件类型判断函数(避免上传漏洞 )
Jun 08 PHP
php数组函数序列之in_array() 查找数组值是否存在
Oct 29 PHP
基于xcache的配置与使用详解
Jun 18 PHP
Yii使用find findAll查找出指定字段的实现方法
Sep 05 PHP
php中使用Ajax时出现Error(c00ce56e)的详细解决方案
Nov 03 PHP
Yii中使用PHPExcel导出Excel的方法
Dec 26 PHP
PHP实现多文件上传的方法
Jul 08 PHP
PHP之正则表达式捕获组与非捕获组(详解)
Jul 29 PHP
PHP去除空数组且数组键名重置的讲解
Feb 28 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获取访客IP、地区位置、浏览器及来源页面等信息
2017/06/27 PHP
详解thinkphp中的volist标签
2018/01/15 PHP
jQuery中add()方法用法实例
2015/01/08 Javascript
jQuery实现TAB风格的全国省份城市滑动切换效果代码
2015/08/24 Javascript
创建一个类Person的简单实例
2016/05/17 Javascript
Bootstrap的基本应用要点浅析
2016/12/19 Javascript
Nodejs--post的公式详解
2017/04/29 NodeJs
JavaScript闭包_动力节点Java学院整理
2017/06/27 Javascript
详解vue.js数据传递以及数据分发slot
2018/01/20 Javascript
微信小程序select下拉框实现效果
2019/05/15 Javascript
createObjectURL方法实现本地图片预览
2019/09/30 Javascript
细述Javascript的加法运算符的具体使用
2019/10/18 Javascript
vue-router之实现导航切换过渡动画效果
2019/10/31 Javascript
Python3控制路由器——使用requests重启极路由.py
2016/05/11 Python
Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例
2017/08/31 Python
使用python实现ANN
2017/12/20 Python
Python判断两个list是否是父子集关系的实例
2018/05/04 Python
Python pygorithm模块用法示例【常见算法测试】
2018/08/16 Python
Python subprocess库的使用详解
2018/10/26 Python
selenium使用chrome浏览器测试(附chromedriver与chrome的对应关系表)
2018/11/29 Python
python抓取网页内容并进行语音播报的方法
2018/12/24 Python
使用pickle存储数据dump 和 load实例讲解
2019/12/30 Python
利用Python自动化操作AutoCAD的实现
2020/04/01 Python
python代码实现猜拳小游戏
2020/11/30 Python
Fairyseason:为个人和批发商提供女装和配件
2017/03/01 全球购物
印度尼西亚手表和包包商店:Urban Icon
2019/12/12 全球购物
Wiggle澳大利亚:自行车、跑步、游泳商店
2020/11/07 全球购物
毕业自我鉴定
2013/11/05 职场文书
国家励志奖学金获奖感言
2014/01/09 职场文书
大型活动策划方案
2014/01/12 职场文书
军校本科大学生自我评价
2014/01/14 职场文书
2014年司法所工作总结
2014/11/22 职场文书
2014年汽车销售工作总结
2014/12/01 职场文书
《比尾巴》教学反思
2016/02/24 职场文书
街道办残联2016年助残日活动总结
2016/04/01 职场文书
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server