Drupal 8 模块开发 10.4 : PHPUnit Doubles / Mocks / Stubs

蒲公英 提交于 周四, 08/17/2017 - 17:40
Drupal8模块开发

原文地址:
https://docs.acquia.com/articles/drupal-8-phpunit-doubles-mocks-stubs  
 

我们写单元测试的目的是要测试个别组件,例如一个服务。
通常,服务需要系统其他部分的功能。这种情况下,需要制造依赖的一个模拟(Mock)版本,以便于我们把焦点放在测试服务本身。  

创建一个模拟

当你创建一个模拟时,其实你正在创建那个类的一个副本,只是它的所有方法被替换返回 NULL。

例如,如果我们需要一个查询对象,可以使用 getMockBuilder() 和 getMock() 方法取得一个。

$query = $this->getMockBuilder('\Drupal\Core\Entity\Query\QueryInterface')
                       ->getMock();

你能看到我们在本例中传递了类名,之后调用了 getMock()。  

禁用原始构造器
这种模式适用于大多数类。一些类有一个 __construct() 函数,需要被移除。我们知道的最简点方式是用基本模式运行你的测试,如果收到一个错误,就添加 disableOriginalConstructor() 方法移除它。

$query_factory = $this->getMockBuilder('\Drupal\Core\Entity\Query\QueryFactory')
                                    ->disableOriginalConstructor()
                                    ->getMock();

抽象类
某些情况下,你将使用一个抽象类。再试下常规模式,如果出错,可以使用下面的方法:

$filter_plugin = $this->getMockBuilder('\Drupal\filter\Plugin\FilterBase')
                                ->getMockForAbstractClass();

模拟方法
当你需要从模拟返回测试值时,你可以为任意类方法创建置换。

$query = $this->getMockBuilder('\Drupal\Core\Entity\Query\QueryInterface')
                       ->getMock();

$query->expects($this->any())->method('execute')->willReturn([1 => 1]);

// or
$query->expects($this->any())->method('execute')->will($this->returnValue([1 => 1]));

PHPUnit 里,有很多可选的方式写东西。第一种实现是第二种的速写。两种写法都是告诉系统: 如果调用查询对象的 execute 方法,会返回数组 [1 => 1] 。大多数情况下,把 $this->any() 作为条件传递足够了。对于较复杂的模拟,你可以发送另一些值。  

返回映射(maps)的模拟方法
前一个例子,我们从模拟方法返回了一个值。我们也可以根据输入返回不同的值。

这个例子返回一个映射数组。数组中的第一项是传递给 get 方法的参数,最后一项是返回值。我们告诉模拟对象当 $fieldDefinition->get('field_name') 被调用时返回字符串 test_field 。类似的,当 $fieldDefinition->get('field_type') 被调用时,返回 text 。如果方法有两个输入,你返回的映射应该是这样: ['argument_one', 'argument_two', 'return_value']。

$fieldDefinition = $this->getMockBuilder('\Drupal\Core\Field\BaseFieldDefinition')
                                    ->getMock();

$fieldDefinition->expects($this->any())->method('get')
  ->willReturnMap([
    ['field_name', 'test_field'],
    ['field_type', 'text'],
    ['fieldStorage', $fieldStorage],
  ]);

使用回调的模拟方法
如果你需要做复杂处理,或者访问可能变化的类变量,你可以提供一个函数作为返回值。新版本 PHP 支持匿名函数,因此你可以把函数声明作为参数。

$this->field = $this->getMockBuilder('\Drupal\Core\Field\FieldItemList')
                              ->disableOriginalConstructor()
                              ->getMock();

$this->field->expects($this->any())->method('getValue')
  // We use a function here so that the value is calculated at runtime.
  ->will($this->returnCallback(
    function() {
      return $this->testValue;
    }
  ));