PHPUnit单元测试对桩件(stub)和仿件对象(Mock)的理解

来源:http://blog.csdn.net/loophome/article/details/52198716


一、桩件(stub)和仿件对象(Mock)概念

桩件(stub):

将对象替换为(可选地)返回配置好的返回值的测试替身的实践方法称为上桩(stubbing)。可以用桩件(stub)来“替换掉被测系统所依赖的实际组件,这样测试就有了对被测系统的间接输入的控制点。这使得测试能强制安排被测系统的执行路径,否则被测系统可能无法执行”。

仿件对象(Mock):

将对象替换为能验证预期行为(例如断言某个方法必会被调用)的测试替身的实践方法称为模仿(mocking)。
可以用 仿件对象(mock object)“作为观察点来核实被测试系统在测试中的间接输出。通常,仿件对象还需要包括桩件的功能,因为如果测试尚未失败则仿件对象需要向被测系统返回一些值,但是其重点还是在对间接输出的核实上。因此,仿件对象远不止是桩件加断言,它是以一种从根本上完全不同的方式来使用的”(Gerard Meszaros)。

二、简单的测试例子

我们先建立一个简单的测试例子,以购买水果为例。

IFruit接口:

[php]  view plain  copy
  1. namespace Test;  
  2.   
  3. interface IFruit  
  4. {  
  5.     //获取水果的单格  
  6.     public function getPrice();  
  7.     //计算购买$number个单位的水果是需要的价格  
  8.     public function buy($number);  
  9. }  
Apple类
[php]  view plain  copy
  1. namespace Test;  
  2.   
  3. class Apple implements IFruit  
  4. {  
  5.     public function getPrice()  
  6.     {  
  7.         return 5.6;  
  8.     }  
  9.       
  10.     public function buy($number)  
  11.     {  
  12.         return $number * $this->getPrice();  
  13.     }  
  14. }  
Custom类

[php]  view plain  copy
  1. namespace Test;  
  2.   
  3. class Custom  
  4. {  
  5.     private $totalMoney;  
  6.       
  7.     public function __construct($money)  
  8.     {  
  9.         $this->totalMoney = $money;  
  10.     }  
  11.       
  12.     public function buy(IFruit $fruit$number)  
  13.     {  
  14.         $needMoney = $fruit->buy($number);  
  15.         if ($needMoney > $this->totalMoney) {  
  16.             return false;  
  17.         } else {  
  18.             $this->totalMoney -= $needMoney;  
  19.             return true;  
  20.         }  
  21.     }  
  22. }  
接下来,我们为Custom类写一个单元测试类,测试购买10个单位的苹果,由于苹果单价为5.6元,即需要56元。但是Custom只有50元,无法购买,因此testBuy方法断言为false。
[php]  view plain  copy
  1. use \Test\Apple;  
  2. use \Test\Custom;  
  3.   
  4. class CustomTest extends PHPUnit_Framework_TestCase  
  5. {  
  6.     protected $custom;  
  7.       
  8.     public function setUp()  
  9.     {  
  10.         $this->custom = new Custom(50);  
  11.     }  
  12.       
  13.     public function testBuy()  
  14.     {  
  15.         $result = $this->custom->buy(new Apple(), 10);  
  16.         $this->assertFalse($result);  
  17.     }  
  18.       
  19. }  

这个测试用例,看起来并没有什么问题,运行起来也是非常正常。但是,可以发现一点就是Custom的测试用例中,把Apple类耦合到一起了。假如Apple获取单价的方法是从某些数据源中获取,那么测试Custom类还需要Apple类的运行环境,增加单元测试的开发难度。

三、使用桩件(stub)减少依赖

使用桩件后的测试用例:

[php]  view plain  copy
  1. public function testBuy()  
  2. {  
  3.     $appleStub = $this->createMock(Apple::class);  
  4.     $appleStub->method("buy")  
  5.               ->willReturn(5.6 * 10);  
  6.     $result = $this->custom->buy($appleStub, 10);  
  7.     $this->assertFalse($result);  
  8. }  

$appleStub是一个桩件,模拟了Apple类的buy方法,并且强制指定了返回值56,减少了对Apple类的依赖。

四、仿件对象(Mock)

使用仿件对象,主要的功能是验证预期的行为,作为观察点来核实被测试系统在测试中的间接输出。

[php]  view plain  copy
  1. public function testMockBuy()  
  2. {  
  3.     $appleMock = $this->getMockBuilder(Apple::class)  
  4.                       ->setMethods(['getPrice''buy'])  
  5.                       ->getMock();  
  6.     //建立预期情况,buy方法会被调用一次  
  7.     $appleMock->expects($this->once())  
  8.               ->method('buy');  
  9.     $this->custom->buy($appleMock, 10);  
  10. }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值