【问题标题】:phpunit: mocking abstract class for type hinting?phpunit:模拟类型提示的抽象类?
【发布时间】:2018-10-06 12:40:17
【问题描述】:

我正在尝试测试一个辅助函数,它采用 Form 的一些实例,这是一个具有单个抽象方法 process 的抽象类(实际上并不是从测试函数调用的)。

function get_field_class(Form $form, $field) {
  return $form->get_errors_for($field) ? 'error' : '';
}

我的问题是,我怎样才能模拟抽象 Form 类,以便它被 get_field_class 的类型注释所接受?

我试过直接传递模拟实例:

$form = $this->getMockForAbstractClass(Form::class)
  ->method('process')
  ->will($this->returnValue(true));

$this->assertEquals('error', $this->wrapper->get_field_class($form, 'foo'));

这会引发 TypeError:参数 1...必须是 Form 的实例,给定的 PHPUnit\Framework\MockObject\Builder\InvocationMocker 实例

我已尝试明确设置模拟类名称:

$form = $this->getMockBuilder(Form::class)
  ->setMockClassName(Form::class)
  ->getMockForAbstractClass(Form::class)
  // ...

这会引发警告“表单”类已存在。 (警告被提升为我的配置中的异常或错误,因此这不是可接受的解决方案。)

我试过禁用自动加载(也许 phpunit 出于某种原因急切地加载 Form 类??):

$form = $this->getMockBuilder(Form::class)
  ->disableAutoload()
  ->setMockClassName(Form::class)
  ->getMockForAbstractClass(Form::class)
  // ...

这只是抛出警告:Class "Form" 不存在。

【问题讨论】:

    标签: mocking phpunit


    【解决方案1】:

    在您的第一个不起作用的示例中,您实际上并没有传递模拟对象。为了使其工作,代码需要:

    $form = $this->getMockForAbstractClass(Form::class);
    
    $form->expects($this->once())
      ->method('process')
      ->will($this->returnValue(true));
    
    $this->assertEquals('error', $this->wrapper->get_field_class($form, 'foo'));
    

    will() 返回一个对象,PHPUnit 使用该对象来验证模拟对象是否被正确调用。

    正如 cmets 中所述,从 MockBuilder 调用中删除 setMockClassName 会起作用。

    $form = $this->getMockBuilder(Form::class)
      ->getMockForAbstractClass(Form::class);
    

    再次使用此表单,您需要将调用的期望分别设置为 process

    您得到的错误是因为该类已经存在并且 PHP 不允许您创建两个具有相同名称的类。无论如何,您都不需要为此设置名称,因为 PHPUnit 将创建一个扩展您的 Form 类的新对象。

    查看 PHPUnit 文档中的示例: https://phpunit.readthedocs.io/en/7.1/test-doubles.html#mocking-traits-and-abstract-classes

    <?php
    use PHPUnit\Framework\TestCase;
    
    abstract class AbstractClass
    {
        public function concreteMethod()
        {
            return $this->abstractMethod();
        }
    
        public abstract function abstractMethod();
    }
    
    class AbstractClassTest extends TestCase
    {
        public function testConcreteMethod()
        {
            $stub = $this->getMockForAbstractClass(AbstractClass::class);
    
            $stub->expects($this->any())
                 ->method('abstractMethod')
                 ->will($this->returnValue(true));
    
            $this->assertTrue($stub->concreteMethod());
        }
    }
    ?>
    

    【讨论】:

    • 这一切都说得通。简而言之,我的主要错误是认为will() 是返回存根对象的“流利”接口的一部分。这让我走上了一条涉及setMockClassName() 和朋友的更复杂的道路。谢谢!
    猜你喜欢
    • 2011-03-14
    • 2016-04-21
    • 2021-10-31
    • 1970-01-01
    • 2012-12-19
    • 2013-04-16
    • 1970-01-01
    • 2011-12-23
    • 1970-01-01
    相关资源
    最近更新 更多