已命名静态构造函数的 PHPUnit 模拟 class

PHPUnit mock class that has named static constructor

假设我有一个 FruitSalad class(被测系统):

class FruitSalad
{
    protected $fruits = [];

    public function addFruit(Fruit $fruit)
    {
        $this->fruits[] = $fruit;
        return $this;
    }
}

我有一个 Fruit class:

class Fruit
{
    public static function withName($name)
    {
        $instance = new MyDependencyClass();
        $instance->name = $name;
        return $instance;
    }
}

一个简单的例子,但是你可以看到 Fruit class 使用命名的静态构造函数,addFruit() FruitSalad [=38= 上的方法] 输入提示 Fruit 作为其预期参数。

在为 addFruit() 编写测试时,我需要模拟 Fruit class。

function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
    $fruit = $this->getMockBuilder('Fruit')
        ->disableOriginalConstructor()
        ->getMock();

    $this->fruitSalad->addFruit($fruit);
    // Do some assertion.
}

这创建了 Fruit class 的简单模拟,但我想通过 withName() 静态方法实例化它 - 我不想公开 setter 为 name 属性。

如何使用静态命名构造函数为 Fruit 创建模拟?

How can I create a mock for Fruit using the static named constructor?

你不能。模拟是使用模拟框架创建的。

无论如何,如何创建模拟并不重要,重要的是它们的行为方式,因为它们在 class 被测试的外部。

只需配置模拟,使其行为方式与使用 Fruit::withName 创建的真实 Fruit 实例相同。

PHPUnit 过去支持模拟静态方法,但从 PHPUnit 4.0 开始它被省略了。我在这里看到四个选项:

1.完全不要模拟该方法

您可以只调用该方法并使用它的逻辑,尽管如果您这样做,您也会测试静态方法,通常这是您在编写单元测试时应该避免的事情。

2。更改 class

问问自己这个方法是否真的需要静态,如果不需要,更改 class 以正确测试它。在很多用例中,最好更改一些架构以编写正确的测试。

3。使用间谍 class

Spy classes 是 classes,它扩展了您通常会模拟的 class,但实现了一些逻辑来测试 class 或测试方法对另一种方法的依赖性。在大多数情况下,可以通过正确模拟 class 来避免这种情况。如果 mocks 不够用,间谍只是你的解决方法,很少有你真正需要它们的情况。

但是,在这种情况下,可以使用间谍来覆盖静态方法作为变通方法:

class FruitSpy extends Fruit
{

    public static $return;

    public static $name;

    public static function withName($name) {
        $expected = self::$name;
        if($name == $expected) {
            return self::$return;
        } else {
            throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
        }
    }

}

此示例检查正确的 $name,如果正确,return 就是您定义的 return。您可以在测试中像这样使用它:

$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();

$this->fruitSalad->addFruit($fruitSpy);

不完全是一个干净的解决方案,但我看到您是否绝对肯定不想更改测试代码以外的其他代码的唯一方法。

同样,如果你需要做这样的事情,你应该考虑将静态方法更改为临时方法。

4。使用 PHPUni 3.*

您可以简单地使用已弃用的 PHPUnit 版本来使用此方法。也不是首选方式。

结论

我没有看到一个干净的方法来模拟静态方法,并且 ::staticExpects() 在 4.0

中出于某种原因被删除了