Jest 中的“toBe”和“toEqual”有啥区别?

Posted

技术标签:

【中文标题】Jest 中的“toBe”和“toEqual”有啥区别?【英文标题】:What is the difference between 'toBe' and 'toEqual' in Jest?Jest 中的“toBe”和“toEqual”有什么区别? 【发布时间】:2017-12-24 23:43:55 【问题描述】:

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 在这种情况下会失败?

此外,是否有使用 toBetoEqual 的最佳实践(不仅在 Jest 中,在其他测试框架中也是如此)?

【问题讨论】:

仅供参考:关于这两种方法的文档现在很常见: - jestjs.io/docs/en/expect#toequalvalue - jestjs.io/docs/en/expect#tobevalue 两种方法都在内部使用Object.is 【参考方案1】:

它失败了,因为xy 是不同的实例,并且不等于(x === y) === false。您可以将toBe 用于字符串、数字或布尔值等原语,其他所有内容都使用toEqual。例如

x = 4 
y = 4
x === y // true

x = 'someString'
y = 'someString'
x === y // true

即使是空对象也不相等

x = 
y = 
x === y //false

【讨论】:

有什么理由使用toBe toBe 还是toBe,这是个问题。 @ThomasChampion 不要开玩笑...toBe() || not.toBe(),就是测试。 问题 = 成为 || !toBe; @OliverShaw toBetoEqual 可以与原语一起使用,但 toBe 特别适用于引用相等。当你想断言它实际上是一个对象的同一个实例时。【参考方案2】:

假设有两个同名玩家,他们都得了 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

我已经针对特定情况编写了此答案,当您了解身份和功能时,您可以在您的场景中实现它。

【讨论】:

【参考方案3】:

自从Jest v23 之后,您还拥有toStrictEqual()

解释:https://jestjs.io/docs/en/expect#tostrictequalvalue

有一个ESLint plugin for Jest 带有一个强制执行toStrictEqual() 的规则: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'
  

【讨论】:

【参考方案4】:

有人说.toBe()x === y是一样的,其实还是有一点区别的。 Jest 在做expect(x).toBe(y) 时使用Object.is(x, y)

MDN - Object.is()

Jest .toBe source code

除非您要验证值是否与引用相同(例如检查某些内容是否正确进行了深度克隆),否则应始终使用.toEqual()。即使在 deepclone 示例中,我认为只做expect(x === y).toEqual(true) 只是为了消除对您要做什么的任何困惑。

您不应该期望其他人知道toBetoEqual 之间的区别,甚至不应该知道Object.is 的存在以及它与=== 的不同之处。为避免通信问题和测试问题,请始终使用.toEqual,切勿使用.toBe

【讨论】:

【参考方案5】:

都是关于对象引用的。

.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' ; 让 b = a;

这里,赋值运算符将存储在变量a中的对象引用复制到b

expect(a).toBe(b); // PASSES

断言通过是因为 'a' 和 'b' 指向同一个对象。请注意 name: 'john doe' 不是对象,它是创建对象的指令。对象存在于内存中,并通过存储在变量中的引用进行交互。

【讨论】:

以上是关于Jest 中的“toBe”和“toEqual”有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

是否有忽略 jest.js 中元素位置的数组相等匹配函数?

vue and jest测试

Jest 中的 describe 和 it 有啥区别?

前端自动化测试框架Jest中的Mock

Jest.js 强制窗口未定义

Jest.js强制窗口未定义。