Symfony ObjectProphecy 以不同的方式对待数组和对象 - 无法更改在 setUp 方法中初始化的数组

Symfony ObjectProphecy treats array and object differently - can't change array which was initialised in setUp method

提问人:user_51 提问时间:9/19/2023 更新时间:9/20/2023 访问量:35

问:

相关

我正在尝试这个测试代码

<?php

use Monolog\Test\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophet;

class MyBar {
    public int $bar = 0;
}

class MyFoo {
    public function fooArray(): array {
        return [];
    }

    public function fooBar(): MyBar {
        return new MyBar();
    }

    public function fooInt(): int {
        return 0;
    }
}

final class SimpleTest extends TestCase
{
    private Prophet $prophet;
    private ObjectProphecy $mock;
    private array $myArray;
    private MyBar $myBar;
    private int $myInt;

    public function getProphet(): Prophet {
        return $this->prophet;
    }

    protected function setUp(): void {
        $this->prophet = new Prophet();
        $this->myArray = [0];
        $this->myBar = new MyBar();
        $this->myInt = 0;
        $this->mock = $this->getProphet()->prophesize(MyFoo::class);
        $this->mock->fooArray()->willReturn($this->myArray);
        $this->mock->fooBar()->willReturn($this->myBar);
        $this->mock->fooInt()->willReturn($this->myInt);
    }
    
    public function test(): void {
        $this->myArray = [1];
        $this->assertEquals([0], $this->mock->reveal()->fooArray());

        $this->myBar->bar = 1;
        $this->assertEquals(1, $this->mock->reveal()->fooBar()->bar);

        $this->myInt = 1;
        $this->assertEquals(0, $this->mock->reveal()->fooInt());
    }
}


请注意测试函数的区别,数组不会更新,int 也不会更新,但对象会更新。$myArray$myInt$myBar

  • 为什么他们不以同样的方式行事?
  • 让数组像对象一样工作的推荐方法是什么?

我正在考虑将数组作为引用传递,但据,使用 .我在顶部链接的问题的答案建议使用另一种方法的解决方案,但我试图看看 中是否有解决方案。ObjectProphecyMockObjectObjectProphecy

数组 symfony 传递引用 预言

评论


答:

0赞 Såpe 9/20/2023 #1

您正在测试完全不同的东西(将西红柿与马铃薯进行比较)。希望我在下面已经很好地解释了这种情况。

为了回答你的第一个问题,我举了一个例子(但是,这也涉及$this->myInt$this->myArray);

在函数中,您使用三个步骤:setup()

  1. 第一步是将 0 分配给 $this->myInt

    $this->myInt = 0;

  2. 第二步是创建一个对象:

    $this->mock = $this->getProphet()->prophesize(MyFoo::class);

  3. 第三步 是分配值为 0 的对象(来自 $this->myInt):
    $this->mock->fooInt()->willReturn($this->myInt);

然后在实际测试函数 ():test()

  1. 作为第四步 ,您将 $this->myInt 值更改为 1

    $this->myInt = 1;

  2. 作为第五步 (#2),您实际上是在测试对象的值,根据步骤 #3,该对象仍应返回 0

    $this->assertEquals(0, $this->mock->reveal()->fooInt());

在最后一步中,您实际上是在与您设置的值进行比较(该值符合 )。例如,您的测试将通过。 没有做任何事情,因为你没有改变你的对象(你正在测试的)的值,你只是在改变你的 .只有当测试 的值时,您的预期行为才有效,因此如下所示:0step #3$this->myInt = 0step #1Step #4class$this->myInt

$this->assertEquals(0, $this->myInt);

因此,要解决您的问题,请重写您的测试或确保您正在更改正确的值(在 ur 中或在 .classobject

为了解释您的示例以及为什么会有所不同,您实际上是在更改 your 中的值并在测试中测试相同的值(正确测试):objectobjectvalue

$this->myBar->bar = 1;
$this->assertEquals(1, $this->mock->reveal()->fooBar()->bar);

您更改了 .再一次,您的测试将通过valueobject

要回答第二个问题,请更改为:step #4

$this->myInt = 1;
$this->mock->fooInt()->willReturn($this->myInt);
// and then to do your test from step #5:
$this->assertEquals(0, $this->mock->reveal()->fooInt());
// this should fail.

评论

0赞 user_51 9/20/2023
感谢您的详细回答,我怀疑可能是这种情况。但是,我仍然不明白为什么他们不以同样的方式行事。你说我改变了我的值,但我可以想象你说我改变了我的值(这足以让我仍然感到困惑)。感觉好像调用或存储了该副本,但是当调用存储引用时(这很公平,因为它不知道如何复制)?myBarobjectmyArrayarrayobjectwillReturnint[int]myBarmyBar
0赞 Såpe 9/21/2023
$this->mock->fooArray()->willReturn($this->myArray);不会返回动态 .它设置(不是)并将始终返回 。在这里,您正在设置一个对象。它将始终返回对象。在这里,您实际上更新了对象中的值。因此,这将被更新,因为您已经更新了对象中的值。$this->myArray[0]$this->myArray[0]$this->mock->fooBar()->willReturn($this->myBar);$this->myBar->bar = 1;$this->mock->reveal()->fooBar()->bar
0赞 user_51 9/21/2023
好吧,我明白了,这符合我的想法,tnx。对我来说,它的行为方式确实有点不自然。出于兴趣,您能否指出实现中可以对 vs 对象 () 进行不同处理的部分?就像我现在测试它一样,我希望它的行为类似于 ,但很高兴确切地知道它在代码中的哪个位置以不同的方式对待它。intmyBarstringintmyBar
0赞 Såpe 9/21/2023
我不能比上面做得更好。但是,我用代码尝试了一下:https://3v4l.org/bv0Z3
0赞 user_51 9/21/2023
啊 - 谢谢!您的代码使我意识到该问题与.第 98 - 108 行说明了我困惑背后的罪魁祸首。而且,为什么这些行会以这种方式工作,仅仅是因为 PHP 赋值运算符 () 以一种方式作用于 , (赋值),而以另一种方式作用于对象 (赋值引用)。这种行为暗示了我所困惑的行为,看不到理性就是这么简单。谢谢你的努力 - 我会把你的答案标记为正确。如果您觉得有用,可以使用此评论的部分内容对其进行更新。willReturn=int[int]willReturn