php单元测试进阶(14)- 核心技术 - 动态mock对象 博客分类: PHP phpunit单元测试php单元测试进阶
程序员文章站
2024-02-16 08:22:52
...
php单元测试进阶(14)- 核心技术 - 动态mock对象
本系列文章主要代码与文字来源于《单元测试的艺术》,原作者:Roy Osherove。译者:金迎。
本系列文章根据php的语法与使用习惯做了改编。所有代码在本机测试通过。如转载请注明出处。
使用手工mock对象的方法可能会遇到问题,包括:
编写mock对象和桩件需要花费时间。
如果类和接口有许多方法,属性和事件,就很难为它编写桩件和mock对象。
难以在其他测试中重用mock类和桩件类。一旦接口有超过两三个方法需要实现,代码维护会很麻烦。
要保存mock对象被多次调用的状态,需要手工实现。
尽管如此,学习手工创建mock对象可以更清楚的学习单元测试的原理,同时,手工编写的mock对象和桩件的代码是更加易读的,并且也确实可以使用。
决定何时用手工创建mock对象,何时用本节介绍的动态mock对象,也是一种艺术。
动态伪对象
动态伪对象是在运行时创建的任何桩件或mock对象,它的创建不需要先硬编码一个类。
动态mock对象的框架
最常用的有两个,一个是PHPUnit_MockObject,这是phpunit自带的默认的mock框架。
另一个是Prophecy,其原本是另一个单元测试框架phpspec的mock类库,但因为很优秀,可以独立使用,可以被内嵌到phpunit当中,并且默认已经有了,无需额外安装。
使用任意一个都可以。本文分别使用一下。
为了学习动态mock,我们需要再仔细读一遍php单元测试进阶(12)- 核心技术 - mock对象。只是我们现在改成动态mock实现。
源代码中,被测类不变,接口也不变。
测试代码中,去除手工mock类文件(少写一个文件!),然后修改测试类如下
测试代码(PHPUnit_MockObject实现)
LogAnalyzerTest.php
cmd下测试通过。
测试代码(Prophecy实现)
LogAnalyzerTest.php
cmd下测试也通过。其实也能发现用Prophecy做动态mock代码很简洁。
现在可以不手工写mock了!
本系列文章结束。
上一篇:php单元测试进阶(13)- 核心技术 - mock对象 - 同时使用mock和stub
本系列文章主要代码与文字来源于《单元测试的艺术》,原作者:Roy Osherove。译者:金迎。
本系列文章根据php的语法与使用习惯做了改编。所有代码在本机测试通过。如转载请注明出处。
使用手工mock对象的方法可能会遇到问题,包括:
编写mock对象和桩件需要花费时间。
如果类和接口有许多方法,属性和事件,就很难为它编写桩件和mock对象。
难以在其他测试中重用mock类和桩件类。一旦接口有超过两三个方法需要实现,代码维护会很麻烦。
要保存mock对象被多次调用的状态,需要手工实现。
尽管如此,学习手工创建mock对象可以更清楚的学习单元测试的原理,同时,手工编写的mock对象和桩件的代码是更加易读的,并且也确实可以使用。
决定何时用手工创建mock对象,何时用本节介绍的动态mock对象,也是一种艺术。
动态伪对象
动态伪对象是在运行时创建的任何桩件或mock对象,它的创建不需要先硬编码一个类。
动态mock对象的框架
最常用的有两个,一个是PHPUnit_MockObject,这是phpunit自带的默认的mock框架。
另一个是Prophecy,其原本是另一个单元测试框架phpspec的mock类库,但因为很优秀,可以独立使用,可以被内嵌到phpunit当中,并且默认已经有了,无需额外安装。
使用任意一个都可以。本文分别使用一下。
为了学习动态mock,我们需要再仔细读一遍php单元测试进阶(12)- 核心技术 - mock对象。只是我们现在改成动态mock实现。
源代码中,被测类不变,接口也不变。
测试代码中,去除手工mock类文件(少写一个文件!),然后修改测试类如下
测试代码(PHPUnit_MockObject实现)
LogAnalyzerTest.php
<?php namespace tests\index\controller; /** * 测试用的类 */ class LogAnalyzerTest extends \think\testing\TestCase { /** * @test * 使用动态mock对象断言,注意:断言代码是写在前面的!! * 注意,尽量使得测试的方法名称有意义,这非常重要,便于维护测试代码。有规律 */ public function analyze_TooShortFileName_CallsWebService() { $tooShortFileName= 'abc.ext'; // 为 IWebService 接口建立mock对象,只模仿 logError() 方法。 $mockService = $this->getMockBuilder('\app\index\controller\IWebService') ->getMock(); // 现在开始断言:预期该mock对象的 logError() 方法将会被调用一次, // 并且将以字符串 "Filename too short:{$tooShortFileName}" 为参数。 $mockService->expects($this->once()) ->method('logError') ->with($this->equalTo("Filename too short:{$tooShortFileName}")); // 创建被测类的对象,注入mock对象 $analyzer = new \app\index\controller\LogAnalyzer($mockService); //调用被测对象,等待断言发生。 $analyzer->analyze($tooShortFileName); } }
cmd下测试通过。
测试代码(Prophecy实现)
LogAnalyzerTest.php
<?php namespace tests\index\controller; /** * 测试用的类 */ class LogAnalyzerTest extends \think\testing\TestCase { /** * @test * 使用动态mock对象断言,注意:断言代码是写在前面的!! * 注意,尽量使得测试的方法名称有意义,这非常重要,便于维护测试代码。有规律 */ public function analyze_TooShortFileName_CallsWebService() { $tooShortFileName= 'abc.ext'; // 为 IWebService 接口建立预言(prophecy)。 $mockService = $this->prophesize('\app\index\controller\IWebService'); // 现在开始断言:预期用该mock类生成的对象的 logError() 方法将会被调用一次, // 并且将以字符串 "Filename too short:{$tooShortFileName}" 为参数。 $mockService->logError("Filename too short:{$tooShortFileName}")->shouldBeCalled(); // 创建被测类的对象,注入mock对象,注意,对象是这里生成,但断言在上面 $analyzer = new \app\index\controller\LogAnalyzer($mockService->reveal()); //调用被测对象,等待断言发生。 $analyzer->analyze($tooShortFileName); } }
cmd下测试也通过。其实也能发现用Prophecy做动态mock代码很简洁。
现在可以不手工写mock了!
本系列文章结束。
上一篇:php单元测试进阶(13)- 核心技术 - mock对象 - 同时使用mock和stub
推荐阅读
-
php单元测试进阶(9)- 核心技术 - 桩件(stub) - 工厂类注入桩件 博客分类: PHP phpunit单元测试php单元测试进阶
-
php单元测试进阶(12)- 核心技术 - mock对象 博客分类: PHP phpunit单元测试php单元测试进阶
-
php单元测试进阶(13)- 核心技术 - mock对象 - 同时使用mock和stub 博客分类: PHP phpunit单元测试php单元测试进阶
-
php单元测试进阶(14)- 核心技术 - 动态mock对象 博客分类: PHP phpunit单元测试php单元测试进阶
-
php单元测试进阶(14)- 核心技术 - 动态mock对象 博客分类: PHP phpunit单元测试php单元测试进阶