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

PHPUnit与Laravel HTTP测试:忽略顺序断言列表(非关联数组)

Laravel HTTP测试中JSON数组的无序断言解决方案

问题场景

在PHPUnit中使用assertEquals()或assertSame()断言数组时,会严格基于键值对进行比较。对于非关联数组(列表),如果元素顺序无关但实际返回顺序与预期不同,这些方法会因为把索引视为键而导致测试失败,通常可以通过自定义断言方法解决:

protected function assertListWithoutOrderEquals(array $expected, array $actual): void
{
    sort($expected);
    sort($actual);

    $this->assertEquals($expected, $actual);
}

但在Laravel HTTP测试中断言JSON响应时,Laravel会自动将JSON转为数组/集合,同样存在顺序敏感的问题。例如测试genres字段时,仅因返回顺序与预期不一致就导致测试失败:

use Illuminate\Testing\Fluent\AssertableJson;

public function test_http_response(): void
{
    $expectedData = [
        'id' => 1,
        'name' => 'The fantastic book',
        'genres' => ['science-fiction', 'drama', 'mystery'],
        // 其他字段
    ];

    $response = $this->get('url');

    $response->assertJson(
        fn(AssertableJson $json) => $json->where('id', $expectedData['id'])
            ->where('name', $expectedData['name'])
            ->where('genres', $expectedData['genres']) // 顺序敏感,会导致测试失败
            ->etc()
    );
}

之前尝试过一种繁琐的写法,利用集合的diff()方法做单向检查,同时校验集合长度来确保双向一致:

->where('genres', fn($genres) => $genres->diff($expectedData['genres'])->isEmpty() && $genres->count() == count($expectedData['genres']))

更简洁的解决方案

方案1:使用PHPUnit的assertEqualsCanonicalizing断言

PHPUnit提供了assertEqualsCanonicalizing()方法,会自动忽略数组/集合的元素顺序,仅比较内容是否完全一致。可以直接在闭包中调用该方法:

->where('genres', fn($genres) => $this->assertEqualsCanonicalizing($expectedData['genres'], $genres->all()))

方案2:利用集合排序后比较

通过对预期数组和返回的集合分别排序,再比较排序后的结果,写法直观简洁:

->where('genres', fn($genres) => 
    $genres->sort()->values()->all() === collect($expectedData['genres'])->sort()->values()->all()
)

方案3:自定义通用断言方法

在项目的TestCase类中添加一个针对集合的无序断言方法,方便在所有测试中复用:

protected function assertCollectionEqualsIgnoringOrder(array $expected, \Illuminate\Support\Collection $actual): void
{
    $this->assertEqualsCanonicalizing($expected, $actual->all());
}

然后在测试中调用:

->where('genres', fn($genres) => $this->assertCollectionEqualsIgnoringOrder($expectedData['genres'], $genres))

内容的提问来源于stack exchange,提问作者Ali Mohammad Yavari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 19:02:45