PHPunit模拟对象,并根据对象属性值对连续调用进行不同的响应

PHPunit模拟对象,并根据对象属性值对连续调用进行不同的响应,php,phpunit,Php,Phpunit,我模拟一个类的函数,并试图根据给定对象(模拟函数的参数)的属性返回不同的值 在我的代码下面,有一些评论: 我嘲笑FacebookHelper类 我对getExternalCampaignData(Campaign$Campaign)函数感兴趣,所以我正在“操纵”它以 我想根据活动id更改返回值 PHP:7.0.18和PHPunit版本:5.7.20 代码 use AppBundle\Entity\Campaign; use Monolog\Logger; use PHPUnit\Framew

我模拟一个类的函数,并试图根据给定对象(模拟函数的参数)的属性返回不同的值

在我的代码下面,有一些评论:

  • 我嘲笑FacebookHelper类
  • 我对getExternalCampaignData(Campaign$Campaign)函数感兴趣,所以我正在“操纵”它以
  • 我想根据活动id更改返回值
  • PHP:7.0.18和PHPunit版本:5.7.20
代码
use AppBundle\Entity\Campaign;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;

class FacebookHelperTest extends TestCase {
    public function testIfStatusSyncIsSetCorrectly() {
        $campaign = new Campaign();

        /** @var \PHPUnit_Framework_MockObject_MockObject|Logger $logger */
        $logger = $this->createMock(Logger::class);

        /** @var \PHPUnit_Framework_MockObject_MockObject|FacebookHelper $helper */
        $builder = $this->getMockBuilder(FacebookHelper::class)->disableOriginalConstructor()->setMethods(['getExternalCampaignData']);
        $helper = $builder->getMock();

        $helper->expects($this->exactly(3))
            ->method('getExternalCampaignData')
            ->withConsecutive(
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 1"); return $campaign->getFbCampaignId() == 1; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 2"); return $campaign->getFbCampaignId() == 2; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 3"); return $campaign->getFbCampaignId() == 3; })]
            )
            ->willReturnOnConsecutiveCalls(
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ARCHIVED]]
            );

        $campaign->setFbCampaignId(1);
        $campaign->setStatus('active');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertFalse($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(2);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(3);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));
    }
}
当我运行上面的单元测试时,一切似乎都正常工作。 除了行为有点奇怪之外,回调调用的次数太多了

我已经发现了一个可能导致以下情况的错误: 因此,我将phpunit更新为5.7.20版,希望这个问题在那里得到解决

或者我需要用不同的方法

PHPunit的结果

use AppBundle\Entity\Campaign;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;

class FacebookHelperTest extends TestCase {
    public function testIfStatusSyncIsSetCorrectly() {
        $campaign = new Campaign();

        /** @var \PHPUnit_Framework_MockObject_MockObject|Logger $logger */
        $logger = $this->createMock(Logger::class);

        /** @var \PHPUnit_Framework_MockObject_MockObject|FacebookHelper $helper */
        $builder = $this->getMockBuilder(FacebookHelper::class)->disableOriginalConstructor()->setMethods(['getExternalCampaignData']);
        $helper = $builder->getMock();

        $helper->expects($this->exactly(3))
            ->method('getExternalCampaignData')
            ->withConsecutive(
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 1"); return $campaign->getFbCampaignId() == 1; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 2"); return $campaign->getFbCampaignId() == 2; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 3"); return $campaign->getFbCampaignId() == 3; })]
            )
            ->willReturnOnConsecutiveCalls(
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ARCHIVED]]
            );

        $campaign->setFbCampaignId(1);
        $campaign->setStatus('active');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertFalse($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(2);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(3);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));
    }
}
Testing started at 10:40 AM ...
Nothing to update - your database is already in sync with the current entity metadata.
PHPUnit 5.7.20 by Sebastian Bergmann and contributors.


Expectation failed for method name is equal to <string:getExternalCampaignData> when invoked 3 time(s)
Parameter 0 for invocation #0 AppBundle\Services\FacebookHelper::getExternalCampaignData(AppBundle\Entity\Campaign Object (...)) does not match expected value.
Failed asserting that AppBundle\Entity\Campaign Object &0000000026aee99e00000000359c8973 (
    ...
    'status' => 'paused'
    ...
    'fb_campaign_id' => 2
    ...
    'settings' => Array &1 (
        'fb_status_out_of_sync' => false
    )
    ...
) is accepted by specified callback.
 /home/ali/www/src/AppBundle/Services/FacebookHelper.php:1227
 /home/ali/www/tests/AppBundle/Services/FacebookHelperTest.php:41

int(1)
string(11) "expecting 1"
int(1)
string(11) "expecting 1"
int(2)
string(11) "expecting 2"
int(2)
string(11) "expecting 1"


Time: 702 ms, Memory: 6.00MB


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Process finished with exit code 1
测试于上午10:40开始。。。
无需更新-您的数据库已与当前实体元数据同步。
PHPUnit 5.7.20由塞巴斯蒂安·伯格曼和贡献者撰写。
调用3次时,方法名称等于的预期失败
调用#0 AppBundle\Services\FacebookHelper::getExternalCampaignData(AppBundle\Entity\Campaign Object(…)的参数0与预期值不匹配。
断言AppBundle\Entity\Campaign对象&00000000 26AEE99E00000000359C8973失败(
...
“状态”=>“已暂停”
...
“fb_活动id”=>2
...
“设置”=>数组和1(
“fb\U状态\u不同步”=>错误
)
...
)由指定的回调函数接受。
/home/ali/www/src/AppBundle/Services/FacebookHelper.php:1227
/home/ali/www/tests/AppBundle/Services/FacebookHelperTest.php:41
int(1)
字符串(11)“应为1”
int(1)
字符串(11)“应为1”
int(2)
字符串(11)“应为2”
int(2)
字符串(11)“应为1”
时间:702ms,内存:6.00MB
失败!
测试:1,断言:1,失败:1。
进程已完成,退出代码为1

Oke,所以在大量搜索之后,我检查了phpunit测试本身的单元测试(paradox:p?),最后发现了它是如何工作的

解决方案

use AppBundle\Entity\Campaign;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;

class FacebookHelperTest extends TestCase {
    public function testIfStatusSyncIsSetCorrectly() {
        $campaign = new Campaign();

        /** @var \PHPUnit_Framework_MockObject_MockObject|Logger $logger */
        $logger = $this->createMock(Logger::class);

        /** @var \PHPUnit_Framework_MockObject_MockObject|FacebookHelper $helper */
        $builder = $this->getMockBuilder(FacebookHelper::class)->disableOriginalConstructor()->setMethods(['getExternalCampaignData']);
        $helper = $builder->getMock();

        $helper->expects($this->exactly(3))
            ->method('getExternalCampaignData')
            ->withConsecutive(
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 1"); return $campaign->getFbCampaignId() == 1; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 2"); return $campaign->getFbCampaignId() == 2; })],
                [$this->callback(function(Campaign $campaign) { var_dump($campaign->getFbCampaignId(), "expecting 3"); return $campaign->getFbCampaignId() == 3; })]
            )
            ->willReturnOnConsecutiveCalls(
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]],
                ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ARCHIVED]]
            );

        $campaign->setFbCampaignId(1);
        $campaign->setStatus('active');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertFalse($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(2);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));

        $campaign->setFbCampaignId(3);
        $campaign->setStatus('paused');

        $modifiedCampaign = $helper->modifyCampaignIfNeeded($campaign, $logger);
        $this->assertTrue($modifiedCampaign->getSettings('fb_status_out_of_sync'));
    }
}
$helper->expects($this->exactly(3))
            ->method('getExternalCampaignData')
            ->will(
                $this->returnCallback(
                    function(Campaign $campaign) {
                        if($campaign->getFbCampaignId() == 1) return ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]];
                        if($campaign->getFbCampaignId() == 2) return ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ACTIVE]];
                        if($campaign->getFbCampaignId() == 3) return ['campaign' => ['status' => FacebookHelper::CAMPAIGN_STATUS_ARCHIVED]];
                    }
                )
            );

您可以将此简化为

$statuses = [
    1 => FacebookHelper::CAMPAIGN_STATUS_ACTIVE,
    2 => FacebookHelper::CAMPAIGN_STATUS_ACTIVE,
    3 => FacebookHelper::CAMPAIGN_STATUS_ARCHIVED,
];

$helper
    ->expects($this->exactly(count($statuses))
    ->method('getExternalCampaignData')
    ->willReturnCallback(function(Campaign $campaign) use ($statuses) {
        $id = $campaign->getFbCampaignId();

        if (!array_key_exists($id, $statuses)) {
            return;
        }

        return [
            'campaign' => [
                'status' => $statuses[$id],
            ],
        ];
    });

在闭包中,
$class
未定义。很抱歉,我更改了答案,但忘记编辑所有变量。谢谢通知,我刚改了。谢谢,很好的缩短版本。