Jest 文档内容如下:
toBe 只是检查一个值是否符合您的期望。它使用 === 来检查严格相等。
对于 toEqual
:
如果要检查两个对象是否具有相同的值,请使用 .toEqual。这个匹配器递归地检查所有字段的相等性,而不是检查对象的身份——这也被称为“深度相等”。例如,toEqual 和 toBe 在这个测试套件中的行为不同,所以所有的测试都通过了。
const x = { a: { b: 3 } };
const y = { a: { b: 3 } };
expect(x).toEqual(y);
expect(x).toBe(y);
在这种情况下,toEqual
通过但 toBe
失败。我知道 toEqual
通过了,因为它进行了深度相等检查。为什么在这种情况下 toBe
会失败?
此外,是否有使用 toBe
和 toEqual
的最佳实践(不仅在 Jest 中,在其他测试框架中也是如此)?
Object.is
。
它失败的原因是 x
和 y
是不同的实例,并且不等于 (x === y) === false
。您可以将 toBe
用于字符串、数字或布尔值等原语,其他所有内容都使用 toEqual
。例如
x = 4
y = 4
x === y // true
x = 'someString'
y = 'someString'
x === y // true
即使是空的对象也不相等
x = {}
y = {}
x === y //false
假设有两个同名玩家,他们都得了 20 分。
let player1 = {
name: "Amit",
score: 20,
}
let player2 = {
name: "Amit",
score: 20,
}
现在我有一个功能可以让我成为第一个玩家。
function getFirstPlayer(player1,player2){
return player1;
}
我将如何测试这个功能?
# 1st way
expect(getFirstPlayer(player1,player2)).toBe(player1); // Passes
expect(getFirstPlayer(player1,player2)).not.toBe(player2); // Passes
# 2nd way
expect(getFirstPlayer(player1,player2)).toEqual(player1); // Pases
expect(getFirstPlayer(player1,player2)).not.toEqual(player2); // Fails
toBe
测试身份和 toEqual
测试功能。因此,双胞胎孩子可以具有相同的特征,但他们的真实身份彼此不同。这个函数的设计方式我们应该使用toBe
。
现在我有另一个增加玩家分数的功能。
function addScore(player,scoreToAdd){
player.score += scoreToAdd;
}
我将如何测试这个功能?
# 1st way
addScore(player1,20);
expect(player1).toBe({name:"Amit", score:40}); // Fails
# 2nd way
addScore(player1,20);
expect(player1).toEqual({name:"Amit", score:40}); // Passes
你有没有注意到,在第一种方式中,我们在右手边传递了一个像实体一样的新玩家。 player1
是否有可能与新创建的实体具有相同的身份?没有。所以 toBe
在这种情况下总是会失败。
与 toEqual
一样,第二路通过我们正在比较特征。这里 player1
和新创建的实体具有相同的特征。
注意: 在 javascript 上下文中,像 "Amit"
这样的原始值本身就是标识。所以
expect("Amit").toBe("Amit") // Passes
我已经为特定情况编写了这个答案,当您了解身份和功能时,您可以在您的场景中实现它。
自Jest v23以来,您还拥有toStrictEqual()
说明:https://jestjs.io/docs/en/expect#tostrictequalvalue
有一个具有强制执行 toStrictEqual()
的规则的 ESLint plugin for Jest:https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/prefer-strict-equal.md
npm install --save-dev eslint-plugin-jest
// .eslintrc.js
module.exports = {
extends: [
'plugin:jest/recommended'
],
rules: {
'jest/prefer-strict-equal': 'error'
}
}
这都是关于对象引用的。
.toBe
比较原始值或检查对象实例的引用身份,而 toEqual
寻找深度相等。
expect({ name: 'john doe' }).toEqual({ name: 'john doe'}); // PASSES
expect({ name: 'john doe' }).toBe({ name: 'john doe'}); // FAILS
第二个断言失败了,因为它们是不同的实例,即使它们非常相等。请记住,对象字面量语法会创建基础对象的新实例。
let a = { name: 'john doe' };
let b = a;
这里,赋值运算符将存储在变量 a
中的对象引用复制到 b
。
expect(a).toBe(b); // PASSES
断言通过是因为 'a' 和 'b' 指向同一个对象。请注意,{ name: 'john doe' }
不是对象,它是创建对象的指令。对象存在于内存中,并通过存储在变量中的引用与之交互。
有人说 .toBe()
与 x === y
相同,但实际上略有不同。 Jest 在执行 expect(x).toBe(y)
时使用 Object.is(x, y)
。
MDN - Object.is()
Jest .toBe 源代码
除非您要验证值是否与引用相同(例如检查某些内容是否正确进行了深度克隆),否则应始终使用 .toEqual()
。即使在 deepclone 示例中,我认为只需执行 expect(x === y).toEqual(true)
以消除对您要执行的操作的任何混淆,这样会更干净。
您不应该期望其他人知道 toBe
和 toEqual
之间的区别,甚至不应该知道 Object.is
的存在以及它与 ===
的区别。为避免通信问题和测试问题,请始终使用 .toEqual
,切勿使用 .toBe
。
toBe
?toBe
还是toBe
,这是个问题。toBe()
||not.toBe()
,即测试。toBe
或toEqual
可以与原语一起使用,但toBe
特别适用于引用相等。当你想断言它实际上是一个对象的同一个实例时。