返回

Laravel单元测试:如何校验JSON响应中数组元素的ID?

php

Laravel 单元测试:如何校验 JSON 响应中数组元素的 ID?

在 Laravel 应用开发中,API 接口返回的 JSON 数据校验是单元测试的重要环节。我们经常需要验证数组中每个对象的特定属性,例如 ID。你可能已经尝试过使用 where 方法和通配符,却发现无法奏效。本文将深入解析这个问题,并提供一种简洁有效的解决方案。

问题分析

假设我们有一个接口返回如下 JSON 数据:

{
  "items": [
    {
      "id": 10,
      "field": "foo"
    },
    {
      "id": 20,
      "field": "bar"
    }
  ]
}

我们希望在测试用例中验证 items 数组中每个元素的 id 是否与预期相符,例如 [10, 20]。你或许尝试过如下代码:

$response->assertJson(fn (AssertableJson $json) =>
    $json->where('items.*.id', [10, 20])
);

这段代码的意图是美好的,但事与愿违。where 方法只能用于断言 JSON 中某个路径的值等于预期值,并不能直接用于比较数组元素。

解决方案详解

为了解决这个问题,我们可以借助 Laravel 提供的 each 方法和闭包函数,对数组进行遍历,并对每个元素进行断言。

以下是完整的测试代码示例:

use Illuminate\Testing\Fluent\AssertableJson;

// ...

$response->assertJson(function (AssertableJson $json) {
  $json->has('items', 2); // 验证 items 数组存在且包含 2 个元素

  $json->where('items', function (AssertableJson $json) {
    $json->each(function (AssertableJson $json) {
      $json->whereType('id', 'integer'); // 验证 id 是整数类型
    });
  });

  $expectedIds = [10, 20]; // 预期的 ID 数组

  $json->where('items.*.id', function ($ids) use ($expectedIds) {
    // 使用 PHPUnit 的 assertArraySubset 方法进行断言
    $this->assertArraySubset($expectedIds, $ids);
  });
});

这段代码的核心逻辑可以拆解如下:

  1. has 方法用于验证 items 数组存在且包含 2 个元素,确保数据结构符合预期。
  2. each 方法遍历 items 数组,为每个元素执行闭包函数内的断言逻辑。
  3. each 闭包中,whereType 方法验证 id 属性的类型为整数,保证数据类型正确。
  4. where 方法获取所有 items.*.id 的值,将其传递给一个新的闭包函数,用于最终的断言。
  5. 新的闭包函数中,PHPUnit 的 assertArraySubset 方法闪亮登场,断言获取到的 ID 数组是否包含所有预期值,而无需考虑顺序。

代码解读

  • assertArraySubset($expectedIds, $ids) 方法是我们的杀手锏,它判断 $expectedIds 数组中的所有元素是否都存在于 $ids 数组中,无需考虑顺序,完美匹配我们的需求。

总结

通过上述方法,我们可以在 Laravel 单元测试中轻松校验 JSON 响应中数组元素的 ID。这种方法灵活易懂,可以根据实际需求进行调整,例如校验其他属性或添加更多断言条件。

常见问题解答

  1. 问:为什么不能直接使用 where 方法和通配符?

    答:where 方法用于断言 JSON 中某个路径的值等于预期值,无法直接用于比较数组元素。

  2. 问:each 方法的作用是什么?

    答:each 方法用于遍历数组,并对每个元素执行指定的闭包函数。

  3. 问:assertArraySubset 方法有什么作用?

    答:assertArraySubset 方法用于断言一个数组是否是另一个数组的子集,不考虑顺序。

  4. 问:除了校验 ID,还能校验其他属性吗?

    答:当然可以,可以在 each 闭包中添加更多断言条件,例如 where('field', 'foo')

  5. 问:这种方法可以用于其他测试场景吗?

    答:当然可以,只要涉及到对 JSON 响应数组元素进行断言,都可以考虑使用这种方法。