【问题标题】:How to mock a constructor with Mockery如何使用 Mockery 模拟构造函数
【发布时间】:2015-08-15 00:14:53
【问题描述】:

我需要测试一下,代码会创建一个具有某些参数的类的新实例:

$bar = new ProgressBar($output, $size);

我尝试创建一个别名模拟并为__construct 方法设置一个期望,但它不起作用:

$progressBar = \Mockery::mock('alias:' . ProgressBar::class);
$progressBar->shouldReceive('__construct')
    ->with(\Mockery::type(OutputInterface::class), 3)
    ->once();

这个期望永远不会满足:

嘲弄\异常\无效计数异常:方法 __construct(object(Mockery\Matcher\Type), 3) 来自 Symfony\Component\Console\Helper\ProgressBar 应该被准确地调用 1 次,但调用了 0 次。

您知道如何使用 Mockery 进行测试吗?

【问题讨论】:

  • 你的构造函数是否包含业务逻辑?如果是这样,那么它可能不应该,因为构造函数只应该真正关心将对象初始化为有效的初始状态。这通常只是将参数分配给成员变量,这很简单,不需要测试。
  • 我不太确定是否正确理解了您的问题。你想创建一个 Mock 但不知道如何传递构造函数参数?还是您真的要检查是否调用了构造函数方法?因为那将是非常非常糟糕的做法 - 这不是它的工作原理。或者您正在测试一种创建 ProgressBar 对象的方法?然后你应该重构它,以便将 PrograssBar 对象作为参数传递给该方法或作为包含该方法的类的构造函数参数(依赖注入)。
  • 我正在为 Symfony 控制台编写一个简单的命令。我想测试在执行命令期间是否初始化了具有正确单位数的 ProgressBar。
  • @VaclavSir 好的,这不太可能,例如php单元。有一种叫做“方面模拟”的东西允许这样的场景,但仅仅因为你可以并不意味着你应该。正如我上面提到的,请阅读“依赖注入”。执行此操作的 OOP 方法是将 PrograssBar 对象传递给方法,而不是在方法中创建它。
  • 不,不会,DI 方式是注入工厂。然而,直接实例化 ProgressBar 是一种常见的做法,工厂除了return new ProgressBar(...func_get_args()) 不会做任何事情。所以我想知道这种做法是否可以用 Mockery 来测试。我会看看 AspectMock。

标签: php unit-testing mockery


【解决方案1】:

你不能模拟构造函数。相反,您需要稍微修改您的生产代码。我可以从描述中猜到你有这样的东西:

class Foo {
    public function bar(){
        $bar = new ProgressBar($output, $size);
    }
}

class ProgressBar{
    public function __construct($output, $size){
        $this->output = $output;
        $this->size = $size;
    }
}

这不是世界上最好的代码,因为我们有耦合依赖。 (例如,如果 ProgressBar 是值对象,这完全可以)。

首先,您应该将ProgressBarFoo 分开测试。因为那时你测试Foo 你不需要关心ProgressBar 是如何工作的。你知道它有效,你对此进行了测试。

但是,如果您仍然想测试它的实例化(出于任何原因),这里有两种方法。对于这两种方式,您都需要提取 new ProggresBar

class Foo {
    public function bar(){
        $bar = $this->getBar($output, $size);
    }

    public function getBar($output, $size){
        return new ProgressBar($output, $size)
    }
}

方式一:

class FooTest{
    public function test(){
        $foo = new Foo();
        $this->assertInstanceOf(ProgressBar::class, $foo->getBar(\Mockery::type(OutputInterface::class), 3));
    }
}

方式二:

class FooTest{
    public function test(){
        $mock = \Mockery::mock(Foo::class)->makePartial();
        $mock->shouldReceive('getBar')
            ->with(\Mockery::type(OutputInterface::class), 3)
            ->once();
    }
}

测试愉快!

【讨论】:

    猜你喜欢
    • 2021-12-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2016-04-22
    • 1970-01-01
    • 2011-02-27
    相关资源
    最近更新 更多