You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHPUnit中同一方法多次调用返回不同结果的正确测试方式

问题:如何测试同一方法(getNextUniqueBunch)使其返回不同结果?
function exampleQuestion() {
  $rows = [];
  $rowsAgain = [];

  while ($bunch = $this->_dataSourceModel->getNextUniqueBunch()) {
            $rows[] = $bunch;
  }

  //执行一些修改后,getNextUniqueBunch将返回不同结果

  while ($bunch = $this->_dataSourceModel->getNextUniqueBunch()) {
            $rowsAgain[] = $bunch;
  }
}

用户尝试的方案1

public function importDataProvider(): array
{
    return [
        [
            [
                '_email' => 'jondoe@example.com',
                '_website' => 'base',
                '_store'=> 'admin',
                '_entity_id' => 'abc'
            ]
        ],
        [
            [
                '_email' => 'jondoe@example.com',
                '_store' => 'admin',
                '_entity_id' => 'abc'
            ]
        ]
    ];
}

/**
 * @dataProvider importDataProvider
 * @return void
 */
public function testImportData($data)
{
    $this->dataSourceModel->expects($this->any())
        ->method('getNextUniqueBunch')
        ->withConsecutive([], [])
        ->willReturnOnConsecutiveCalls([$data], [$data]);
   $mockClassObj->exampleQuestion(); 
}

该方案中,第一次调用getNextUniqueBunch返回数组,但第二次调用返回空数组,无法满足第二次循环获取数据的需求。

用户尝试的方案2

修改代码后,第二次调用getNextUniqueBunch可正常返回结果:

$this->dataSourceModel->expects($this->at(3))
        ->method('getNextUniqueBunch')
        ->withConsecutive([], [])
        ->willReturnOnConsecutiveCalls([$data], [$data]);

此方案使用了已弃用的$this->at(3),虽然能让第二次调用返回数据,但不符合PHPUnit的最佳实践。

正确实现方式

问题核心是要模拟两次完整的循环调用序列:第一次循环先返回数据,再返回空值终止循环;第二次循环再次返回新数据,再返回空值终止循环。直接用PHPUnit的willReturnOnConsecutiveCalls按调用顺序指定所有返回值即可,无需依赖弃用的at()方法:

/**
 * @dataProvider importDataProvider
 * @return void
 */
public function testImportData($dataFirstLoop, $dataSecondLoop)
{
    // 按调用顺序指定返回值:第一次循环返回数据→空值终止;第二次循环返回新数据→空值终止
    $this->dataSourceModel->expects($this->exactly(4))
        ->method('getNextUniqueBunch')
        ->willReturnOnConsecutiveCalls(
            [$dataFirstLoop],  // 第一次循环取数
            null,              // 第一次循环终止
            [$dataSecondLoop], // 第二次循环取数
            null               // 第二次循环终止
        );

    $mockClassObj->exampleQuestion();

    // 可添加断言验证结果,示例:
    // $this->assertEquals([[$dataFirstLoop]], $mockClassObj->rows);
    // $this->assertEquals([[$dataSecondLoop]], $mockClassObj->rowsAgain);
}

// 调整数据提供者,提供两次循环的不同数据
public function importDataProvider(): array
{
    return [
        [
            // 第一次循环返回的数据
            [
                '_email' => 'jondoe@example.com',
                '_website' => 'base',
                '_store'=> 'admin',
                '_entity_id' => 'abc'
            ],
            // 第二次循环返回的数据
            [
                '_email' => 'jondoe@example.com',
                '_store' => 'admin',
                '_entity_id' => 'abc'
            ]
        ]
    ];
}

关键说明:

  • $this->exactly(4)明确指定方法被调用4次,避免any()带来的匹配模糊问题;
  • willReturnOnConsecutiveCalls严格遵循业务代码中两次循环的调用顺序返回对应值;
  • 若getNextUniqueBunch无数据时返回空数组而非null,只需把代码中的null替换为[]即可。

内容的提问来源于stack exchange,提问作者rocky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:58:24