PHPUnit单元测试

一、概述

1. 什么是单元测试?

  • 【百度百科】单元测试是对软件中的最小可测单元进行检查和验证。
  • 是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。

2. 作用是什么?

  • 【废话】检查软件、程序的可行性,稳定性。
  • 通过单元测试能够避免在迭代、升级等过程中,引起重复的、多余的问题。
  • 避免在别人修改代码的时候,影响到你的逻辑

3. 哪些程序需要写单元测试(PHP)?

  • 【理想】理想的单元测试应当覆盖程序中所有可能的路径,包括正确的和错误的路径,个单元测试通常覆盖一个函数或方法中的一个特定路径。
  • 【现实】model、helper、controller中的函数必须测试、路径覆盖到所有可能性

二、PHPUnit的安装与集成CI框架

  • 略。。。。。后续再补

三、PHPUnit的使用

编写测试用例

  1. 测试的依赖关系 @depends
    PHPUnit支持对测试方法之间的显式依赖关系进行声明。这种依赖关系并不是定义在测试方法的执行顺序中,而是允许生产者(producer)返回一个测试基境(fixture)的实例,并将此实例传递给依赖于它的消费者(consumer)们。

    <?phpclass StackTest extends PHPUnit_Framework_TestCase{public function testEmpty(){$stack = array();$this->assertEmpty($stack);return $stack;}/*** @depends testEmpty*/public function testPush(array $stack){array_push($stack, 'foo');$this->assertEquals('foo', $stack[count($stack)-1]);$this->assertNotEmpty($stack);return $stack;}/*** @depends testPush*/public function testPop(array $stack){$this->assertEquals('foo', array_pop($stack));$this->assertEmpty($stack);}}                       ?>
    
  2. 默认情况下,生产者所产生的返回值将“原样”传递给相应的消费者。这意味着,如果生产者返回的是一个对象,那么传递给消费者的将是一个指向此对象的引用。如果需要传递对象的副本而非引用,则应当用 @depends clone 替代 @depends。
  3. 数据供给器 @dataProvider 测试方法可以接受任意参数。用 @dataProvider 标注来指定使用哪个数据供给器方法。 数据供给器方法必须声明为 public,其返回值要么是一个数组,其每个元素也是数组;要么是一个实现了 Iterator 接口的对象,在对它进行迭代时每步产生一个数组。每个数组都是测试数据集的一部分,将以它的内容作为参数来调用测试方法。
 <?php
class DataTest extends PHPUnit_Framework_TestCase
{/*** @dataProvider additionProvider*/public function testAdd($a, $b, $expected){$this->assertEquals($expected, $a + $b);}public function additionProvider(){return array(array(0, 0, 0),array(0, 1, 1),array(1, 0, 1),array(1, 1, 3));}
}
?>也可以是这样:<?php
class DataTest extends PHPUnit_Framework_TestCase
{/*** @dataProvider additionProvider*/public function testAdd($a, $b, $expected){$this->assertEquals($expected, $a + $b);}public function additionProvider(){return array('adding zeros' => array(0, 0, 0),'zero plus one' => array(0, 1, 1),'one plus zero' => array(1, 0, 1),'one plus one' => array(1, 1, 3));}
}
?>
对PHP错误进行测试
默认情况下,PHPUnit 将测试在执行中触发的 PHP 错误、警告、通知都转换为异常。利用这些异常,就可以,比如说,预期测试将触发 PHP 错误<?php
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{/*** @expectedException PHPUnit_Framework_Error*/public function testFailingInclude(){include 'not_existing_file.php';}
}
?>  测试phpunit -d error_reporting=2 ExpectedErrorTestPHPUnit 5.2.0 by Sebastian Bergmann and contributors..Time: 0 seconds, Memory: 5.25MbOK (1 test, 1 assertion)

命令行测试执行器

说明

PHPUnit 测试执行器可通过phpunit 调用,例如在CI中:
tongkundeMacBook-Pro:www tongkun$ cd tests/
tongkundeMacBook-Pro:tests tongkun$ phpunit
PHPUnit 5.0.0 by Sebastian Bergmann and contributors..............                                                     13 / 13 (100%)Time: 195 ms, Memory: 17.50MbOK (13 tests, 8 assertions)

说明:

先进入测试的根目录,执行phpunit 命令,后面可跟具体的目录或文件,也可不跟,如果没有则会对当前目录的所有文件执行单元测试,对于每个测试的运行,PHPUnit命令行工具会输出一个字符来指示进展:

  • . 当测试陈宫时输出
  • F 当测试方法运行过程中一个断言失败时输出,例如一个失败的assertEquals()调用
  • E 当测试方法运行过程中产生一个错误时输出,错误是指意料之外的异常(exception)或者PHP错误
  • R 当测试被标记有风险时输出
  • S 当测试跳出时输出
  • I 当测试被标记不完整或为实现时输出

常用命令行选项

  • --coverage-clover:为运行的测试生成带有代码覆盖率信息的 XML 格式的日志文件
  • --coverage-html:生成 HTML 格式的代码覆盖率报告
  • --coverage-php:生成一个序列化后的 PHP_CodeCoverage 对象,此对象含有代码覆盖率信息
  • --log-json:生成 JSON 格式的日志文件
  • --filter:只运行名称与给定模式匹配的测试。如果模式未闭合包裹于分隔符,PHPUnit 将用 / 分隔符对其进行闭合包裹
      tongkundeMacBook-Pro:tests tongkun$ phpunit --filter 'WelcomeTest::testTest'
  • PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
  • ...                                                                 3 / 3 (100%)
  • Time: 148 ms, Memory: 16.75Mb
  • OK (3 tests, 3 assertions)
  • 这样测试的就是WelcomeTest类中的testTest函数,过滤模式的例子有很多,详见文档官方
  • --colors:使用彩色输出。Windows下,用 ANSICON 或 ConEmu。
    本选项有三个可能的值:
    never: 完全不使用彩色输出。当未使用 --colors 选项时,这是默认值。
    auto: 如果当前终端不支持彩色、或者输出被管道输出至其他命令、或输出被重定向至文件时,不使用彩色输出,其余情况使用彩色。
    always: 总是使用彩色输出,即使当前终端不支持彩色、输出被管道输出至其他命令、或输出被重定向至文件。
    当使用了 --colors 选项但未指定任何值时,将选择 auto 做为其值。
  • --stop-on-error:首次错误出现后停止执行。
  • --stop-on-failure:首次错误或失败出现后停止执行。
  • --stop-on-risky:首次碰到有风险的测试时停止执行。
  • --stop-on-risky:首次碰到有风险的测试时停止执行。
  • --stop-on-incomplete首次碰到不完整的测试时停止执行。
  • --repeat:将测试重复运行指定次数。
      tongkundeMacBook-Pro:tests tongkun$ phpunit --repeat 10
  • PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
  • ...............................................................  63 / 130 ( 48%)
  • ............................................................... 126 / 130 ( 96%)
  • ....                                                            130 / 130 (100%)
  • Time: 456 ms, Memory: 26.25Mb
  • OK (130 tests, 80 assertions)
  • --tap:使用 Test Anything Protocol (TAP) 报告测试进度
      tongkundeMacBook-Pro:tests tongkun$ phpunit --tap
  • TAP version 13
  • ok 1 - CI_Unit_Test_class_Test::test_CI_Unit_Test_Class
  • ok 2 - SomeControllerTest::testWelcomeController
  • ok 3 - WelcomeTest::testIndex
  • ok 4 - WelcomeTest::testTest
  • ok 5 - WelcomeTest::testOutput
  • ok 6 - WelcomeTest::testTest1
  • ok 7 - WelcomeTest::testTest2
  • ok 8 - HelperTest::testSampleFunction
  • ok 9 - SomeLibTest::testMethod
  • ok 10 - M_user_masterTest::testSelect
  • ok 11 - M_user_masterTest::testInsert
  • ok 12 - PHPTest::testFunctionJsonEncode
  • ok 13 - PHPTest::testPhpVersion
  • 1..13
  • --configuration, -c:从 XML 文件中读取配置信息。更多细节请参见附录 C。
    如果 phpunit.xml 或 phpunit.xml.dist (按此顺序)存在于当前工作目录并且未使用 --configuration,将自动从此文件中读取配置。
      tongkundeMacBook-Pro:tests tongkun$ phpunit --configuration phpunit.xml
  • PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
  • .............                                                     13 / 13 (100%)
  • Time: 209 ms, Memory: 17.50Mb
  • OK (13 tests, 8 assertions)
  • --no-configuration:忽略当前工作目录下的 phpunit.xml 与 phpunit.xml.dist。

四、基境(fixture)

什么是基境?

“基境”就是编写代码来将整个场景设置成某个已知的状态,并在测试结束后将其复原到初始状态。这个已知的状态称为测试的 基境(fixture)。

基境的建立

PHPUnit 支持共享建立基境的代码。在运行某个测试方法前,会调用一个名叫 setUp() 的模板方法。setUp() 是创建测试所用对象的地方。当测试方法运行结束后,不管是成功还是失败,都会调用另外一个名叫 tearDown() 的模板方法。tearDown() 是清理测试所用对象的地方。

<?php
class StackTest extends PHPUnit_Framework_TestCase
{protected $stack;protected function setUp(){$this->stack = array();}public function testEmpty(){$this->assertTrue(empty($this->stack));}public function testPush(){array_push($this->stack, 'foo');$this->assertEquals('foo', $this->stack[count($this->stack)-1]);$this->assertFalse(empty($this->stack));}public function testPop(){array_push($this->stack, 'foo');$this->assertEquals('foo', array_pop($this->stack));$this->assertTrue(empty($this->stack));}
}
?>

测试类的每一个方法都会运行一次setUp()和tearDown()模板方法(同时,每个测试方法都在一个全新的测试类实例上运行), 另外,setUpBeforeClass() 与 tearDownAfterClass() 模板方法将分别在测试用例类的第一个测试运行之前和测试用例类的最后一个测试运行之后调用。基境共享可以在共享数据库连接时使用;

五、组织测试

用文件系统来编排测试套件

例如:

phpunit controllers/WelcomeTest.php

用 XML 配置来编排测试套件

<?xml version="1.0" encoding="UTF-8"?><phpunit //配置文件colors="true" //颜色stopOnFailure="false" //出错后是否终止 bootstrap="../application/third_party/CIUnit/bootstrap_phpunit.php"> //bootstrap 地址<php><server name="SERVER_NAME" value="http://www.nyhdev.com" /></php><testsuites>//测试套件<testsuite name="ControllerTests"><directory>controllers</directory> //要测试的目录</testsuite><testsuite name="HelperTests"><directory suffix=".php">helpers</directory></testsuite><testsuite name="LibTests"><directory suffix=".php">libs</directory></testsuite><testsuite name="ModelTests"><directory suffix=".php">models</directory></testsuite><testsuite name="SystemTests"><directory suffix=".php">system</directory></testsuite></testsuites><filter><blacklist><directory>vendor</directory><directory>libs</directory></blacklist><whitelist><directory>controllers</directory><directory>fixtures</directory><directory>models</directory><directory>helpers</directory></whitelist>
</filter>
</phpunit>

六、有风险的测试

无用测试

PHPUnit 可以更严格对待事实上不测试任何内容的测试。此项检查可以用命令行选项 --report-useless-tests 或在 PHPUnit 的 XML 配置文件中设置 beStrictAboutTestsThatDoNotTestAnything="true" 来启用。

在启用本项检查后,如果某个测试未进行任何断言,它将被标记为有风险。仿件对象中的预期和诸如 @expectedException 这样的标注同样视为断言。

测试执行期间产生的输出

PHPUnit 可以更严格对待测试执行期间产生的输出。 此项检查可以用命令行选项 --disallow-test-output 或在 PHPUnit 的 XML 配置文件中设置 beStrictAboutOutputDuringTests="true" 来启用。

在启用本项检查后,如果某个测试产生了输出,例如,在测试代码或被测代码中调用了 print,它将被标记为有风险。

七、未完成的测试与跳过的测试

未完成的测试

开始写新的测试用例类时,可能想从写下空测试方法开始,比如:

public function testSomething()
{
}

假如把成功的测试视为绿灯、测试失败视为红灯,那么还额外需要黄灯来将测试标记为未完成或尚未实现。PHPUnit_Framework_IncompleteTest 是一个标记接口,用于将测试方法抛出的异常标记为测试未完成或目前尚未实现而导致的结果。PHPUnit_Framework_IncompleteTestError 是这个接口的标准实现。

例如:我们有一个测试文件,contrllers/WelcomeTest.php,其中有一个测试方法,通过在测试方法中调用markTestIncomplete()将这个测试标记为未完成。

public function testTest() {$this->assertTrue(true,'这里可以正常工作');$this->markTestIncomplete('此测试尚未实现');
}

在PHPUnit命令行测试执行器中输出,未完成的测试标记为1, 如下:

localhost:tests tongkun$ phpunit
PHPUnit 5.0.0 by Sebastian Bergmann and contributors.R..I...RRRR..                                                     13 / 13 (100%)Time: 187 ms, Memory: 17.75MbOK, but incomplete, skipped, or risky tests!
Tests: 13, Assertions: 8, Incomplete: 1, Risky: 5.

跳过测试

如上,如果有些测试需要某些环境或者配置才能完成,则可选择跳过,通过调用 markTestSkipped() 方法来测试

用 @requires 来跳过测试

除了上述方法,还可以用 @requires 标注来表达测试用例的一些常见前提条件。 事例:

例 7.3: 用 @requires 来跳过测试

<?php
/*** @requires extension mysqli*/
class DatabaseTest extends PHPUnit_Framework_TestCase
{/*** @requires PHP 5.3*/public function testConnection(){// 测试要求有 mysqli 扩展,并且 PHP >= 5.3}// ... 所有其他要求有 mysqli 扩展的测试
}
?>

要求安装mysqli苦战和php 5.3 才能执行

#常用断言

前边废话一篇,终于到了关键的断言部分,断言可以说是单元测试的核心,通过断言的校验,保证程序的正确运行,并输出正确的值。

  • assertArrayHasKey()
    assertArrayHasKey(mixed $key, array $array[, string $message = ''])
    当 $array 不包含 $key 时报告错误,错误讯息由 $message 指定。
    assertArrayNotHasKey() 是与之相反的断言,接受相同的参数。
  • assertContains()
    assertContains(mixed $needle, Iterator|array $haystack[, string $message = ''])
    当 $needle 不是 $haystack的元素时报告错误,错误讯息由 $message 指定。
    assertNotContains() 是与之相反的断言,接受相同的参数。
    assertContains(string $needle, string $haystack[, string $message = '', boolean $ignoreCase = FALSE])
    当 $needle 不是 $haystack 的子字符串时报告错误,错误讯息由 $message 指定。
  • assertContainsOnly()
    assertContainsOnly(string $type, Iterator|array $haystack[, boolean $isNativeType = NULL, string $message = ''])
    当 $haystack 并非仅包含类型为 $type 的变量时报告错误,错误讯息由 $message 指定。
    $isNativeType 是一个标志,用来表明 $type 是否是原生 PHP 类型。
  • assertEmpty()
    assertEmpty(mixed $actual[, string $message = ''])
    当 $actual 非空时报告错误,错误讯息由 $message 指定。
    assertNotEmpty() 是与之相反的断言,接受相同的参数。
    assertAttributeEmpty() 和 assertAttributeNotEmpty() 是便捷包装(convenience wrapper),可以应用于某个类或对象的某个 public、protected 或 private 属性。
  • assertEquals()
    assertEquals(mixed $expected, mixed $actual[, string $message = ''])
    当两个变量 $expected 和 $actual 不相等时报告错误,错误讯息由 $message 指定。
    assertNotEquals() 是与之相反的断言,接受相同的参数。
    注意特定类型的比较(浮点型等),详见文档
  • assertFalse()
    assertFalse(bool $condition[, string $message = ''])
    当 $condition 为 TRUE 时报告错误,错误讯息由 $message 指定。
    assertNotFalse() 是与之相反的断言,接受相同的参数。
  • assertNull()
    assertNull(mixed $variable[, string $message = ''])
    当 $actual 不是 NULL 时报告错误,错误讯息由 $message 指定。
    assertNotNull() 是与之相反的断言,接受相同的参数。
  • assertRegExp() assertRegExp(string $pattern, string $string[, string $message = ''])
    当 $string 不匹配于正则表达式 $pattern 时报告错误,错误讯息由 $message 指定。
    assertNotRegExp() 是与之相反的断言,接受相同的参数。
  • assertStringMatchesFormat()
    assertStringMatchesFormat(string $format, string $string[, string $message = ''])
    当 $string 不匹配于 $format 定义的格式时报告错误,错误讯息由 $message 指定。
    assertStringNotMatchesFormat() 是与之相反的断言,接受相同的参数。
  • assertSame() assertSame(mixed $expected, mixed $actual[, string $message = ''])
    当两个变量 $expected 和 $actual 的值与类型不完全相同时报告错误,错误讯息由 $message 指定。
    assertNotSame() 是与之相反的断言,接受相同的参数。
    assertAttributeSame() 和 assertAttributeNotSame() 是便捷包装(convenience wrapper),以某个类或对象的某个 public、protected 或 private 属性作为实际值来进行比较。
  • assertTrue()
    assertTrue(bool $condition[, string $message = ''])
    当 $condition 为 FALSE 时报告错误,错误讯息由 $message 指定。
    assertNotTrue() 是与之相反的断言,接受相同的参数。

PHPUnit单元测试相关推荐

  1. php单元测试工具入门,PHPUnit 单元测试安装与使用入门教程

    本文实例讲述了PHPUnit 单元测试安装与使用.分享给大家供大家参考,具体如下: 一.官网下载对应 PHP 版本的代码库 二.安装 PHPUnit 官网提供了两种方法安装 1. PHP Archiv ...

  2. PHPUnit单元测试 - 我看过的PHP开源框架

    2019独角兽企业重金招聘Python工程师标准>>> PHPUnit 作为XUnit系列,大家应该对单元测试体系都比较了解,这里不再详细说明,感兴趣的同学可以参考PHPUnit官方 ...

  3. phpunit 单元测试案例--签到任务

    因工作需要,最近要写单元测试了,这里算是一个记录的过程吧,慢慢记录,慢慢学习,慢慢总结,早点把这块的信息熟悉起来~~ 之前也写过简单的单元测试的一些小的说明,但是现在的是比较具体的例子了! 这里要列举 ...

  4. PHPUNIT 单元测试

    在windows上的安装可以参考其手册 首先下载phpunit.phar文件 1. 为php的二进制可执行文件建立 一个目录,如C:\bin 2. 将C:\bin添加到系统环境变量中, 3. 打开命令 ...

  5. PHP单元测试框架PHPUnit的使用方法

    以前在学习IOS开发时有专门写过Objective-C的单元测试的文章,IOS开发学习之单元测试,今天再总结下怎么在PHP中使用单元测试. 一.前言 在这篇文章中,我们使用 composer 的依赖包 ...

  6. PHP单元测试框架PHPUnit的使用

    以前在学习IOS开发时有专门写过Objective-C的单元测试的文章,IOS开发学习之单元测试,今天再总结下怎么在PHP中使用单元测试. 一.前言 在这篇文章中,我们使用 composer 的依赖包 ...

  7. [PHPUnit]自动生成PHPUnit测试骨架脚本-提供您的开发效率【2015升级版】

    2019独角兽企业重金招聘Python工程师标准>>> 场景 在编写PHPUnit单元测试代码时,其实很多都是对各个类的各个外部调用的函数进行测试验证,检测代码覆盖率,验证预期效果. ...

  8. 软件测试简介教程:单元测试、黑盒测试、白盒测试

    单元测试背后的原因通常,软件属于四级测试:单元测试,集成测试,系统测试和验收测试,但有时由于时间消耗,软件测试人员进行的单元测试最少,但单元测试的跳过可能会导致集成测试,系统测试期间出现更高的缺陷 , ...

  9. php自动生成phpunit,[PHPUnit]自动生成PHPUnit测试骨架脚本

    场景 在编写PHPUnit单元测试代码时,其实很多都是对各个类的各个外部调用的函数进行测试验证,检测代码覆盖率,验证预期效果.为避免增加开发量,可以使用PHPUnit提供的phpunit-skelge ...

最新文章

  1. 线程常用方法,线程安全和同步锁
  2. 彻底明白IP地址——计算相关地址
  3. Deis发布1.4版本,支持Microsoft Azure
  4. 计算机在娱乐中的应用有哪些,多媒体技术在娱乐方面的应用
  5. C语言实现tolower
  6. oracle 7 客户端,windows 7环境下配置oracle 11g 客户端
  7. update 没有索引导致业务崩了,老板骂了一个小时
  8. @RestControllerAdvice 异常分析
  9. ~~spfa判断图中是否存在负环
  10. java cookie实例_java 中cookie的详解及简单实例
  11. CentOS 7 配置DHCP服务器
  12. css--小米商城----logo与home图标切换
  13. 信息系统项目管理师 - 项目组合管理
  14. 第31次中国互联网络发展状况统计报告
  15. php 开发模式 自定义,smartprinter虚拟打印机 smarty+adodb+部分自定义类的php开发模式...
  16. 传输层端口、TCP和UDP的概念
  17. 计算机启动提示找不到硬盘,开机时硬盘不启动怎么办 找不到硬盘的原因是什么...
  18. 【淘宝】图片放大 代码
  19. 暑假?不进厂?那就卷s同学吧
  20. FZU-1493-ElGamal数字签名-A^X=B(%C)求x

热门文章

  1. UML:类图复习-鸡生蛋,蛋生鸡
  2. 使用struts2框架中3种客户端向服务端发送请求参数的方式
  3. ABAP SALV实现弹出ALV选择
  4. Android简单获取手机联系人姓名电话号码
  5. a key holding the wrong kind of value
  6. 数字化门店转型| 水疗会所管理系统| 小程序搭建
  7. 人工智能正处在一个转折点
  8. 【2022研电赛】安谋科技企业命题三等奖:基于自主跟随的无人结账一体化购物车
  9. u8云服务器系统管理,用友u8云服务器
  10. virtualBox 为虚拟机添加ssd 类型虚拟盘(disk)