1. 概念
什么是基境
首先参考翻译原文
在编写测试时,最费时的部分之一是编写代码来将整个场景设置成某个已知的状态,并在测试结束后将其复原到初始状态。这个已知的状态称为测试的 基境(fixture)。
简单地说,就是为了测试某个方法/函数所需要的环境前提。那么基境就是针对某个测试而言的,其所需要的初始状态。下面举个例子:
<?phpuse PHPUnit\Framework\TestCase;class StackTest extends TestCase{public function testEmpty(){$stack = [];$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);}}?>
在这个例子中,testEmpty的没有基境的需求,基础环境随意;testPush由于依赖的缘故,需要一个空的$stack;testPop依赖于testPush,即需要一个有内容的$stack。
2. 共享基境
有哪些方法
- 每个测试方法的前后
- setUp()
- tearDown()
- 第一个测试之前,与最后一个测试之后
class StackTest extends TestCase { protected $stack;
protected function setUp(){$this->stack = [];}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()函数为每个测试方法建立了共享的基境,而没有使用依赖。但是同时也在各个方法中自行设置了所需的基境。<a name="aVrz8"></a>## 关于setUp()与tearDown()理论上说,他俩应该是对称的。但实践中,只有在setUp中分配了文件、套接字之类的外部资源才需要tearDown。或者是在setUp中创建了大量对象,可以在tearDown中unset掉。<a name="L8r0d"></a>## 关于共享基境文档中有一句话是> 在测试之间共享基境会降低测试的价值。潜在的设计问题是对象之间并非松散耦合。如果解决掉潜在的设计问题并使用桩件(stub)(参见:ref:test-doubles)来编写测试,就能达成更好的结果,而不是在测试之间产生运行时依赖并错过改进设计的机会。暂时还没有具体理解<a name="xkIWq"></a># 3. 关于全局状态使用了全局状态(比如全局变量、类的静态属性、单例模式之类)的代码通常比较难以测试,欲测代码和全局状态之间的耦合度较高,且其创建无法控制。此外,一个测试对全局变量的修改可能会破坏其他的测试。<a name="uGmBH"></a>## 单例模式的测试[拓展阅读](https://testing.googleblog.com/2008/05/tott-using-dependancy-injection-to.html)<a name="tHixz"></a>## 全局变量> PHPUnit版本6对全局变量的处理和之前版本不同,这里仅讨论6以及之后的版本的处理方式PHPUnit提供了方法,可以用来备份与还原全局变量。在PHPUnit6之前的版本中,备份还原操作时默认开启的,之后的版本则默认关闭。可以通过添加参数开启:- 命令行中添加 `--global-backup` ,或者XML配置文件中使用 `backupGlobals="true"` 激活- 命令行中添加 `--static-bakcup` ,或者XML配置文件中使用 `backupStaticAttributes="true"` ,可以将此隔离扩展至静态属性。这个方法对函数内的静态变量是无效的。- 也可以使用 `@backupGlobals enabled/disabled` 对类/测试方法进行标注,具体[点这里](https://phpunit.readthedocs.io/zh_CN/latest/annotations.html#backupglobals)- 但是该方式比较需要注意,若标注在某个测试方法上,其仅仅意味着,这个方法的执行前后,全局变量/静态属性的值不变,而不是会还原为初始声明的默认值。所以对于静态属性,在setUp()中进行显式地重置是比较推荐的<a name="eC7Un"></a>### 测试用例```php分别使用两种方式运行方式一:phpunit --globals-backup ClassTest.php方式二:phpunit ClassTest.php<?phpuse PHPUnit\Framework\TestCase;$t = 1;class ClassTest extends TestCase{public function test1(){global $t;$t += 1;echo $t; //output 2}public function test2(){global $t;echo $t; //with option: 1//without option: 2}}
备份黑名单
PHPUnit提供了添加黑名单的方式,可以在开启备份选项的时候,对特定的全局变量/静态变量不进行备份(仅列出了全局变量的方式,静态属性类似):
- 可以使用
@backupGlobals disabled进行标注,同样需要注意上文列出来的问题 - 也可以在类中添加
protected $backupGlobalsBlacklist = ['globalVariable']- 在方法中对这个变量进行设置是无效的
- 不过在测试的时候,报了warning:
PHPUnit\Framework\TestCase::$backupGlobalsBlacklist is deprecated and will be removed in PHPUnit 10. Please use PHPUnit\Framework\TestCase::$backupGlobalsExcludeList instead.看来官方文档更新的不够及时,使用的时候可以注意一下注意事项
备份还原的操作使用的是serialize()和unserialize()。所以某些类的实例对象(比如PDO)由于无法序列化,无法进行备份4. 综上所述
对于一个测试而言,能为其提供基境的主要方式有
- 依赖关系——通过生产者消费者关系来建立基境
setUp()以及tearDown()设置公共的基境- 数据供给器
- 自行在函数内设置
