1. 基础测试的编写

规则

  1. 对于 Class 类的测试,写在 ClassTest 类中,文件和类同名
  2. ClassTest 类通常继承自 PHPUnit\Framework\TestCase
  3. 测试方法的命名都是 test* 的public方法

也可以在注释中使用 @test 标注

  1. 主要使用assert断言,对实际值和期望值的匹配进行判断

    例子

    ```php <?php use PHPUnit\Framework\TestCase;

//简单说明编写PHPUnit测试的大致方式 //对Stack类进行测试 class StackTest extends TestCase { /*除了函数名的命名方式,也可以使用下面这种方式标注为test方法 @test */ public function testStack(){ $stack = []; $this->assertEquals(0, count($stack));//对值进行判断 } }

  1. <a name="zHOcX"></a>
  2. # 2. 测试的依赖关系
  3. <a name="ySofV"></a>
  4. ## 说明
  5. - 可以通过添加@depends functionname来显式地声明依赖
  6. - 可以从生产者与消费者的角度来理解依赖关系
  7. - 可以简要地理解为,被依赖的函数可以通过返回值传递内容
  8. - 拥有多个 `@depends` 标注的方法,其第一个参数是第一个生产者提供的,以此类推
  9. <a name="qJVQQ"></a>
  10. ## 需要注意的
  11. - PHPUnit**不会**因为依赖关系而更改test的执行顺序,仍然会根据test在代码中的编写顺序进行执行,所以需要自行保证被依赖的方法出现在依赖之前
  12. - 默认情况下,返回值是按照**原样**记性传递,即传对象则将传引用。如果想要传副本,则使用 `@depends clone` 进行代替
  13. - 标注都应使用/****/形式,而不能使用 `//`
  14. <a name="6xq28"></a>
  15. ## 例子
  16. ```php
  17. <?php
  18. use PHPUnit\Framework\TestCase;
  19. class DependsTest extends TestCase
  20. {
  21. public function testA(){
  22. $a = 1;
  23. $this->assertTrue(true); //若没有断言,则依赖于它的testB会被跳过,不执行
  24. return $a;
  25. }
  26. public function testC(){
  27. $this->assertTrue(true);
  28. return 'second';
  29. }
  30. /**
  31. * @depends testA
  32. * @depends testC
  33. */
  34. public function testB($a,$b){
  35. echo $a; //print 1
  36. echo $b; //print second
  37. }
  38. }

3. 数据供给器

说明

  • 测试方法可以接受由数据供给器方法提供的参数
  • 数据供给器方法必须是public;返回值可以是数组,或者是实现了Iterator接口的对象。且数组或者迭代的每一个元素都是数组
  • 可以添加键名,让输出信息更加详细(只影响错误信息的输出,与结果无关)
  • 数据供给器方法的执行,都是在setUpBeforeClass()和setUp()之前完成的,所以在数据供给器中无法使用创建者两个方法的变量

    例子

    ```php <?php use PHPUnit\Framework\TestCase;

class DataProviderTest extends TestCase { /**

  1. * @dataProvider additionProvider
  2. */
  3. public function testAdd($a, $b, $expected)
  4. {
  5. $this->assertEquals($expected, $a + $b);
  6. }
  7. public function additionProvider()
  8. {
  9. return [//key name可以省略,不过可以提供更详细的输出
  10. 'adding zeros' => [0, 0, 0],
  11. 'zero plus one' => [0, 1, 1],
  12. 'one plus zero' => [1, 0, 1],
  13. 'one plus one' => [1, 1, 3]
  14. ];
  15. }

}

  1. ```php
  2. [@hbhly_75_237 TestCode]$ phpunit ClassTest.php
  3. PHPUnit 9.4.2 by Sebastian Bergmann and contributors.
  4. ...F 4 / 4 (100%)
  5. Time: 00:00.004, Memory: 18.00 MB
  6. There was 1 failure:
  7. 1) ClassTest::testAdd with data set "one plus one" (1, 1, 3)
  8. Failed asserting that 2 matches expected 3.
  9. /search/xuyixiang/TestCode/ClassTest.php:11
  10. FAILURES!
  11. Tests: 4, Assertions: 4, Failures: 1.

一些注意事项

  • 如果一个测试依赖于一个使用了数据供给器的测试,则仅当被依赖的测试至少能在一组数据上成功的时候,该测试才会运行。使用了数据供给器的测试,其运行结果无法注入到依赖于此测试的其他测试中,无法提供返回值。如图所示,在该情况下,需要testA至少能成功执行一组数据的时候,testB才会执行,但testA无法向testB提供返回值
  • 如果某方法同时从 dataProvider@depends 中接收数据,那么来自数据供给器的参数将优先于依赖 ```php <?php use PHPUnit\Framework\TestCase;

class DependencyAndDataProviderComboTest extends TestCase { public function provider() { return [[‘provider1’], [‘provider2’]]; }

  1. public function testProducerFirst()
  2. {
  3. $this->assertTrue(true);
  4. return 'first';
  5. }
  6. public function testProducerSecond()
  7. {
  8. $this->assertTrue(true);
  9. return 'second';
  10. }
  11. /**
  12. * @depends testProducerFirst
  13. * @depends testProducerSecond
  14. * @dataProvider provider
  15. */
  16. public function testConsumer()
  17. {
  18. $this->assertEquals(
  19. ['provider2', 'first', 'second'],
  20. func_get_args()
  21. );
  22. }

} ?>

[@hbhly_75_237 TestCode]$ phpunit ClassTest.php PHPUnit 9.4.2 by Sebastian Bergmann and contributors.

..F. 4 / 4 (100%)

Time: 00:00.004, Memory: 18.00 MB

There was 1 failure:

1) ClassTest::testConsumer with data set #0 (‘provider1’) Failed asserting that two arrays are equal. —- Expected +++ Actual @@ @@ Array (

  • 0 => ‘provider2’
  • 0 => ‘provider1’ 1 => ‘first’ 2 => ‘second’ )

/search/xuyixiang/TestCode/ClassTest.php:32

FAILURES! Tests: 4, Assertions: 4, Failures: 1.

  1. <a name="STtBf"></a>
  2. # 4. 对异常进行测试
  3. <a name="1boYy"></a>
  4. ## 可用的方法
  5. - 期望(也属于断言assert)
  6. - $this->expectException()
  7. - 检测是否抛出异常,参数为异常类型
  8. - $this->expectExceptionCode()
  9. - 对抛出异常的code进行判断
  10. - $this->expectExceptionMessage()
  11. - 对异常的Message进行判断
  12. - 这个函数断言的是$actual中包含有$expectedd,并非执行精确的字符串比较
  13. - $this->expectExceptionMessageRegExp()
  14. - 对message可以使用正则表达式进行判断
  15. - 标注(效果等同于使用期望)
  16. - `@expected` +以上那些
  17. <a name="xGOse"></a>
  18. ## 注意
  19. - 先断言,再抛出异常,对错误测试也是同理
  20. <a name="wqZMm"></a>
  21. ## 例子
  22. ```php
  23. <?php
  24. use PHPUnit\Framework\TestCase;
  25. class ClassTest extends TestCase
  26. {
  27. public function testException(){
  28. $this->expectException(Exception::class);
  29. throw new Exception();
  30. }
  31. }

5. 对PHP错误进行测试

官方文档是这么说的:

默认情况下,PHPUnit 将测试在执行中触发的 PHP 错误、警告、通知都转换为异常,这样就可以在测试中触发PHP错误、警告或通知

抛开这段令人费解的话,先看看它所给出来的例子:

  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. //节选改编自官方文档
  4. class ErrorTest extends TestCase
  5. {
  6. public function testNoticeCanBeExpected(): void
  7. {
  8. $this->expectNotice();
  9. // (可选)测试讯息和某个字符串相等
  10. $this->expectNoticeMessage('foo');
  11. // 或者(可选)测试讯息和某个正则表达式匹配
  12. $this->expectNoticeMessageMatches('/foo/');
  13. \trigger_error('foo', \E_USER_NOTICE);
  14. }
  15. public function testWarningCanBeExpected(): void
  16. {
  17. $this->expectWarning();
  18. // (可选)测试讯息和某个字符串相等
  19. $this->expectWarningMessage('foo');
  20. // 或者(可选)测试讯息和某个正则表达式匹配
  21. $this->expectWarningMessageMatches('/foo/');
  22. \trigger_error('foo', \E_USER_WARNING);
  23. }
  24. public function testErrorCanBeExpected(): void
  25. {
  26. $this->expectError();
  27. // (可选)测试讯息和某个字符串相等
  28. $this->expectErrorMessage('foo');
  29. // 或者(可选)测试讯息和某个正则表达式匹配
  30. $this->expectErrorMessageMatches('/foo/');
  31. \trigger_error('foo', \E_USER_ERROR);
  32. }
  33. }

注意到,它所测试的错误是通过 trigger_error 的方式提出的。而这个函数创建的是用户级别的错误消息。所以我暂时推测,官方文档的那段描述,是针对这个情况而言的。

官方文档同时指出:

如果测试代码使用了会触发错误的 PHP 内建函数,比如 fopen,有时候在测试中使用错误抑制符会很有用。
> 如果不使用错误抑制符,此测试将会失败,并报告 > fopen(/is-not-writeable/file): failed to open stream: No such file or directory> 。 也就是说,它并不能直接对这类情形的错误进行测试,想要测试这类情况,需要使用错误抑制符 @

此外,文档也指出

PHP的 error_reporting() 运行时配置会对PHPUnit将哪些错误转换为异常有所限制。如果在这个特性上碰到问题,请确认PHP的配置中没有抑制你所关注的错误类型

即这个函数的报错也是PHPUnit可以测试的,但存在一些问题。

6. 对输出进行测试

对echo、print之类的输出进行测试

例子

  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. class ClassTest extends TestCase
  4. {
  5. public function testFailingInclude()
  6. {
  7. $this->expectOutputString('fo');
  8. print('FO');
  9. }
  10. }
  11. [@hbhly_75_237 TestCode]$ phpunit ClassTest.php
  12. PHPUnit 9.4.2 by Sebastian Bergmann and contributors.
  13. F 1 / 1 (100%)
  14. Time: 00:00.003, Memory: 18.00 MB
  15. There was 1 failure:
  16. 1) ClassTest::testFailingInclude
  17. Failed asserting that two strings are equal.
  18. --- Expected
  19. +++ Actual
  20. @@ @@
  21. -'fo'
  22. +'FO'

方法

方法 含义
void expectOutputRegex(string $regularExpression) 设置输出预期为输出应当匹配正则表达式 $regularExpression
void expectOutputString(string $expectedString) 设置输出预期为输出应当与 $expectedString 字符串相等。
bool setOutputCallback(callable $callback) 设置回调函数,用来做诸如将实际输出规范化之类的动作。
string getActualOutput() 获取实际输出。

在严格模式下,本身产生输出的测试将会失败

7. 错误相关信息输出

PHPUnit关于错误信息的输出

主要讲述的是,PHPUnit在发生测试失败的时候,会尽可能多地提供错误信息输出以及其输出的形式的控制,比如对一个数组进行 assertSame() 断言时,如果发生了不匹配,将会尽可能多地输出其相邻索引的几个value,以供对比。这里只列了比较有意思的一点

  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. class ClassTest extends TestCase
  4. {
  5. //这个不会报错,因为进行的是较弱(宽松)的比较
  6. public function testFailingInclude()
  7. {
  8. $this->assertEquals(
  9. ['1',2],
  10. [1,2]
  11. );
  12. }
  13. //这个会对'1'进行报错
  14. //这种情况只会出现在,对数组或者对象使用assertEquals或其他较弱的比较函数时
  15. public function testFailingInclude2()
  16. {
  17. $this->assertEquals(
  18. ['1',3],
  19. [1,2]
  20. );
  21. }
  22. }
  23. ?>
  24. [@hbhly_75_237 TestCode]$ phpunit ClassTest.php
  25. PHPUnit 9.4.2 by Sebastian Bergmann and contributors.
  26. .F 2 / 2 (100%)
  27. Time: 00:00.004, Memory: 18.00 MB
  28. There was 1 failure:
  29. 1) ClassTest::testFailingInclude2
  30. Failed asserting that two arrays are equal.
  31. --- Expected
  32. +++ Actual
  33. @@ @@
  34. Array (
  35. - 0 => '1'
  36. - 1 => 3
  37. + 0 => 1
  38. + 1 => 2
  39. )
  40. /search/xuyixiang/TestCode/ClassTest.php:21
  41. FAILURES!
  42. Tests: 2, Assertions: 2, Failures: 1.

8. 补充

《xUnit测试模式》:单元测试的四个阶段

  • 建立基境
  • 执行被测系统
  • 验证结果
  • 拆除基境