【问题标题】:Matching arrays returned from sequelize with Jest使用 Jest 匹配从 sequelize 返回的数组
【发布时间】:2020-04-16 04:15:52
【问题描述】:

我正在测试一个 Node.js 应用程序,其中地址使用 Sequelize 存储在 Postgresql 数据库中。为了测试“getAll()”函数,我编写了一个执行以下操作的测试(见下文): 1.创建两个地址,并存储它们(Sequelize的'create'函数) 2.调用getAll()函数 3.(尝试)断言返回的数组包含创建的地址,使用expect.arrayContaining()

我在没有 Sequelize 的情况下测试了相同的设置(将数字/对象添加到数组并以与我在这里相同的方式匹配子集),效果很好。

 it("Should get all addresses", async () => {
     const address_1 = await Address.create({
         street: VALID_STREET,
         number: VALID_NUMBER,
         city: VALID_CITY,
         country: VALID_COUNTRY
     });
     const address_2 = await Address.create({
         street: "street 2",
         number: "2",
         city: "City 2",
         country: "Country 2"
     });

     const allAddresses = await controller._getAll();
     expect(allAddresses).toEqual(
         expect.arrayContaining([address_1, address_2])
     );
 });

我预计测试会成功,但它失败了,给了我这个错误消息,其中包含两个完全相同的数组,除了“ArrayContaining”部分。

expect(received).toEqual(expected) // deep equality

    Expected: ArrayContaining [{"city": "A city", "country": "Belgium", "createdAt": 2019-08-21T14:05:23.063Z, "fullAddress": "valid street 123 A, A city, Belgium", "id": 296, "number": "123 A", "postalCode": null, "street": "valid street", "updatedAt": 2019-08-21T14:05:23.063Z}, {"city": "City 2", "country": "Country 2", "createdAt": 2019-08-21T14:05:23.066Z, "fullAddress": "street 2 2, City 2, Country 2", "id": 297, "number": "2", "postalCode": null, "street": "street 2", "updatedAt": 2019-08-21T14:05:23.066Z}]
    Received: [{"city": "A city", "country": "Belgium", "createdAt": 2019-08-21T14:05:23.063Z, "fullAddress": "valid street 123 A, A city, Belgium", "id": 296, "number": "123 A", "postalCode": null, "street": "valid street", "updatedAt": 2019-08-21T14:05:23.063Z}, {"city": "City 2", "country": "Country 2", "createdAt": 2019-08-21T14:05:23.066Z, "fullAddress": "street 2 2, City 2, Country 2", "id": 297, "number": "2", "postalCode": null, "street": "street 2", "updatedAt": 2019-08-21T14:05:23.066Z}]

那么,有谁知道如何解决这个问题?这是 Sequelize 问题,还是我在这里遗漏了什么?

编辑: 对于遇到同样问题的人,我确实有一个解决方法,即使用 array.some() 函数,但这似乎太冗长了,我觉得应该使用 expect.arrayContaining() 函数.

expect(
    allAddresses.some(address => {
        address.id === address_2.id &&
        address.street === address_2.street &&
        address.number === address_2.number &&
        address.city === address_2.city &&
        address.country === address_2.country;
    })
);

【问题讨论】:

  • 介意分享controller._getAll()的作用吗?
  • 我还问自己为什么在这里使用arrayContaining 而不是expect(allAddresses).toEqual([address_1, address_2])。你添加了两个元素,你想从_getAll 得到这两个元素,不是吗?
  • 当然; _getAll() 方法实际上只是 sequelize 的 findAll() 函数的一个包装器,它以特定于应用程序的方式处理错误,但不会更改返回的数据。至于你的第二个问题:我试过了,但得到了这个错误:Compared values serialize to the same structure. Printing internal object structure without calling `toJSON` instead。然后我尝试将它们转换为 JSON,但这也不起作用(结果与原始问题中的错误相同,除了所有引号都被转义了)
  • 您在findAll 查询中使用raw: true 吗? sequelize.org/master/manual/models-usage.html#raw-queries
  • 感谢您的建议。我只是尝试过,但没有解决它。

标签: arrays node.js jestjs sequelize.js


【解决方案1】:

我认为比较整个模型不是一个好主意(那里有很多你不关心的东西)。尝试比较 dataValues。确保您没有在 findAll 调用中使用 raw: true,因为这样您就没有 dataValues 属性。

const allAddresses = await controller._getAll();
const addressesPlain = allAddresses.map(address => address.dataValues);

expect([address_1.dataValues, address_2.dataValues]).toEqual(addressesPlain);

如果你对我写的小测试用例感兴趣:https://pastr.io/view/QKNmua

【讨论】:

  • 这修复了它!感谢您投入的时间和工作!此外,我发现在数据库中有超过 2 个地址的情况下,使用“toEqual”会失败(显然,因为返回了额外的地址),但这也适用于 expect.arrayContaining(),正如我最初尝试的那样! expect(addressesPlain).toEqual( expect.arrayContaining([address_1.dataValues, address_2.dataValues]) );
  • 不客气。关于toEqual:由于您在测试中控制了所有内容,因此您应该知道断言运行时的项目数。你插入两个,你期望两个。您不应该使用数据库中的其他项目开始此测试。还有一件事:我不确定你想测试什么,但你在这里测试的基本上是“如果我用 sequelize.js 添加两个项目,然后我尝试用 sequelize.js 检索它们,它们仍然是两个并具有相同的内容?”。也许你回过头来想想如何测试你的应用程序,而不是你使用的库。 :-)
  • 是的。我确实压缩了这个测试来显示问题,但我可能确实测试太多了!例如,我正在测试数据库中的约束是否按我的预期工作(尽管这更多是为了防止它们被更改和破坏而不是实际测试它们),您对此有何看法?换句话说,我应该在测试什么被插入(以及如何)和返回方面走多远?
  • 谈到单元测试,我会完全模拟数据库。我会确保我会发送正确的数据,如果我收到数据,我会确保我 a)预期错误的数据,但可以恢复/使用/记录 b)按指定处理数据。确保数据库执行您期望它执行的任何操作可以在集成测试中完成。您可能想阅读“测试金字塔”、“集成测试”和“单元测试”。还要检查 Jest 文档 (jestjs.io/docs/en/mock-functions) 的模拟部分。玩得开心:)
【解决方案2】:

使用 arrayContaining 匹配器的另一种方法,不是更好,只是更明确一点 IMO:

const allAddresses = await controller._getAll();
const addressesPlain = allAddresses.map(address => address.dataValues);

expect(addressesPlain).toEqual(expect.arrayContaining([address_1.dataValues, address_2.dataValues]);

我发现使用 jest 的匹配器在数组中搜索单个项目有点令人困惑,因此仅检查单个对象是否在数组中很有用:

expect(addressesPlain).toContainEqual(address_1.dataValues)

toContainEqual,正如他们的文档所述,“递归地检查所有字段的相等性,而不是检查对象身份。”而 toContain 确实会检查 objectIdentity,但上述检查将失败。

【讨论】:

    猜你喜欢
    • 2021-08-20
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    相关资源
    最近更新 更多