1. 基础测试的编写
规则
- 对于
Class类的测试,写在ClassTest类中,文件和类同名 ClassTest类通常继承自PHPUnit\Framework\TestCase类- 测试方法的命名都是
test*的public方法
也可以在注释中使用 @test 标注
//简单说明编写PHPUnit测试的大致方式 //对Stack类进行测试 class StackTest extends TestCase { /*除了函数名的命名方式,也可以使用下面这种方式标注为test方法 @test */ public function testStack(){ $stack = []; $this->assertEquals(0, count($stack));//对值进行判断 } }
<a name="zHOcX"></a># 2. 测试的依赖关系<a name="ySofV"></a>## 说明- 可以通过添加@depends functionname来显式地声明依赖- 可以从生产者与消费者的角度来理解依赖关系- 可以简要地理解为,被依赖的函数可以通过返回值传递内容- 拥有多个 `@depends` 标注的方法,其第一个参数是第一个生产者提供的,以此类推<a name="qJVQQ"></a>## 需要注意的- PHPUnit**不会**因为依赖关系而更改test的执行顺序,仍然会根据test在代码中的编写顺序进行执行,所以需要自行保证被依赖的方法出现在依赖之前- 默认情况下,返回值是按照**原样**记性传递,即传对象则将传引用。如果想要传副本,则使用 `@depends clone` 进行代替- 标注都应使用/****/形式,而不能使用 `//`<a name="6xq28"></a>## 例子```php<?phpuse PHPUnit\Framework\TestCase;class DependsTest extends TestCase{public function testA(){$a = 1;$this->assertTrue(true); //若没有断言,则依赖于它的testB会被跳过,不执行return $a;}public function testC(){$this->assertTrue(true);return 'second';}/*** @depends testA* @depends testC*/public function testB($a,$b){echo $a; //print 1echo $b; //print second}}
3. 数据供给器
说明
- 测试方法可以接受由数据供给器方法提供的参数
- 数据供给器方法必须是public;返回值可以是数组,或者是实现了Iterator接口的对象。且数组或者迭代的每一个元素都是数组
- 可以添加键名,让输出信息更加详细(只影响错误信息的输出,与结果无关)
- 数据供给器方法的执行,都是在setUpBeforeClass()和setUp()之前完成的,所以在数据供给器中无法使用创建者两个方法的变量
例子
```php <?php use PHPUnit\Framework\TestCase;
class DataProviderTest extends TestCase { /**
* @dataProvider additionProvider*/public function testAdd($a, $b, $expected){$this->assertEquals($expected, $a + $b);}public function additionProvider(){return [//key name可以省略,不过可以提供更详细的输出'adding zeros' => [0, 0, 0],'zero plus one' => [0, 1, 1],'one plus zero' => [1, 0, 1],'one plus one' => [1, 1, 3]];}
}
```php[@hbhly_75_237 TestCode]$ phpunit ClassTest.phpPHPUnit 9.4.2 by Sebastian Bergmann and contributors....F 4 / 4 (100%)Time: 00:00.004, Memory: 18.00 MBThere was 1 failure:1) ClassTest::testAdd with data set "one plus one" (1, 1, 3)Failed asserting that 2 matches expected 3./search/xuyixiang/TestCode/ClassTest.php:11FAILURES!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’]]; }
public function testProducerFirst(){$this->assertTrue(true);return 'first';}public function testProducerSecond(){$this->assertTrue(true);return 'second';}/*** @depends testProducerFirst* @depends testProducerSecond* @dataProvider provider*/public function testConsumer(){$this->assertEquals(['provider2', 'first', 'second'],func_get_args());}
} ?>
[@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.
<a name="STtBf"></a># 4. 对异常进行测试<a name="1boYy"></a>## 可用的方法- 期望(也属于断言assert)- $this->expectException()- 检测是否抛出异常,参数为异常类型- $this->expectExceptionCode()- 对抛出异常的code进行判断- $this->expectExceptionMessage()- 对异常的Message进行判断- 这个函数断言的是$actual中包含有$expectedd,并非执行精确的字符串比较- $this->expectExceptionMessageRegExp()- 对message可以使用正则表达式进行判断- 标注(效果等同于使用期望)- `@expected` +以上那些<a name="xGOse"></a>## 注意- 先断言,再抛出异常,对错误测试也是同理<a name="wqZMm"></a>## 例子```php<?phpuse PHPUnit\Framework\TestCase;class ClassTest extends TestCase{public function testException(){$this->expectException(Exception::class);throw new Exception();}}
5. 对PHP错误进行测试
官方文档是这么说的:
默认情况下,PHPUnit 将测试在执行中触发的 PHP 错误、警告、通知都转换为异常,这样就可以在测试中触发PHP错误、警告或通知
抛开这段令人费解的话,先看看它所给出来的例子:
<?phpuse PHPUnit\Framework\TestCase;//节选改编自官方文档class ErrorTest extends TestCase{public function testNoticeCanBeExpected(): void{$this->expectNotice();// (可选)测试讯息和某个字符串相等$this->expectNoticeMessage('foo');// 或者(可选)测试讯息和某个正则表达式匹配$this->expectNoticeMessageMatches('/foo/');\trigger_error('foo', \E_USER_NOTICE);}public function testWarningCanBeExpected(): void{$this->expectWarning();// (可选)测试讯息和某个字符串相等$this->expectWarningMessage('foo');// 或者(可选)测试讯息和某个正则表达式匹配$this->expectWarningMessageMatches('/foo/');\trigger_error('foo', \E_USER_WARNING);}public function testErrorCanBeExpected(): void{$this->expectError();// (可选)测试讯息和某个字符串相等$this->expectErrorMessage('foo');// 或者(可选)测试讯息和某个正则表达式匹配$this->expectErrorMessageMatches('/foo/');\trigger_error('foo', \E_USER_ERROR);}}
注意到,它所测试的错误是通过 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之类的输出进行测试
例子
<?phpuse PHPUnit\Framework\TestCase;class ClassTest extends TestCase{public function testFailingInclude(){$this->expectOutputString('fo');print('FO');}}[@hbhly_75_237 TestCode]$ phpunit ClassTest.phpPHPUnit 9.4.2 by Sebastian Bergmann and contributors.F 1 / 1 (100%)Time: 00:00.003, Memory: 18.00 MBThere was 1 failure:1) ClassTest::testFailingIncludeFailed asserting that two strings are equal.--- Expected+++ Actual@@ @@-'fo'+'FO'
方法
| 方法 | 含义 |
|---|---|
void expectOutputRegex(string $regularExpression) |
设置输出预期为输出应当匹配正则表达式 $regularExpression。 |
void expectOutputString(string $expectedString) |
设置输出预期为输出应当与 $expectedString 字符串相等。 |
bool setOutputCallback(callable $callback) |
设置回调函数,用来做诸如将实际输出规范化之类的动作。 |
string getActualOutput() |
获取实际输出。 |
在严格模式下,本身产生输出的测试将会失败
7. 错误相关信息输出
PHPUnit关于错误信息的输出
主要讲述的是,PHPUnit在发生测试失败的时候,会尽可能多地提供错误信息输出以及其输出的形式的控制,比如对一个数组进行 assertSame() 断言时,如果发生了不匹配,将会尽可能多地输出其相邻索引的几个value,以供对比。这里只列了比较有意思的一点
<?phpuse PHPUnit\Framework\TestCase;class ClassTest extends TestCase{//这个不会报错,因为进行的是较弱(宽松)的比较public function testFailingInclude(){$this->assertEquals(['1',2],[1,2]);}//这个会对'1'进行报错//这种情况只会出现在,对数组或者对象使用assertEquals或其他较弱的比较函数时public function testFailingInclude2(){$this->assertEquals(['1',3],[1,2]);}}?>[@hbhly_75_237 TestCode]$ phpunit ClassTest.phpPHPUnit 9.4.2 by Sebastian Bergmann and contributors..F 2 / 2 (100%)Time: 00:00.004, Memory: 18.00 MBThere was 1 failure:1) ClassTest::testFailingInclude2Failed asserting that two arrays are equal.--- Expected+++ Actual@@ @@Array (- 0 => '1'- 1 => 3+ 0 => 1+ 1 => 2)/search/xuyixiang/TestCode/ClassTest.php:21FAILURES!Tests: 2, Assertions: 2, Failures: 1.
8. 补充
《xUnit测试模式》:单元测试的四个阶段
- 建立基境
- 执行被测系统
- 验证结果
- 拆除基境
