我的“哇,我不知道!” 开玩笑的时刻

大家好! JavaScript开发人员课程将于本周四开始。 在这方面,我们决定分享另一种有趣的材料的翻译。 好好阅读。



笑话一直是我必不可少的单元测试工具。 它是如此可靠,以至于我开始认为我一直没有充分利用它。 尽管测试有效,但随着时间的流逝,我不时重构了它们,因为我不知道Jest可以做到这一点。 每当我检查Jest文档时,这都是一个新代码。

因此,我将分享一些您可能已经知道的我最喜欢的Jest技巧,这是因为您阅读了文档而不是像我一样(感到羞耻),但是我希望这对那些刚刚通过它的人有所帮助!

顺便说一下,我将Jest v24.8.0用作参考资料,因此请注意,某些事情可能不适用于您当前使用的Jest版本。 另外,这些示例并不代表实际的测试代码,这只是一个演示。

#1 .toBe与.toEqual


最初,所有这些声明对我来说都是正常的:

expect('foo').toEqual('foo') expect(1).toEqual(1) expect(['foo']).toEqual(['foo']) 

基于对等式语句(to.equal)使用chai ,这很自然。 实际上,Jest不会抱怨,这些陈述照常通过。

但是,Jest具有.toBe和.toEqual。 第一个用于使用Object.is声明相等性,第二个用于提供对象和数组的深度比较。 如果事实证明您不需要深层比较(如断言原始值的相等性),. toEqual会使用Object.is ,这可以解释为什么前面的示例运行得很好。

 expect('foo').toBe('foo') expect(1).toBe(1) expect(['foo']).toEqual(['foo']) 

这样,如果您已经知道要测试的值,则可以使用.toBe跳过.toEqual所有if-else
一个常见的错误是您将使用.toBe声明原始值的相等性。

 expect(['foo']).toBe(['foo']) 

如果您在.toBe崩溃时查看源代码 ,它将尝试通过调用 .toEqual使用的函数来确定您是否确实犯了此错误。 优化测试时可能会成为瓶颈。

如果确定使用的是原始值,则可以将代码重新组织为优化目的:

 expect(Object.is('foo', 'foo')).toBe(true) 

文档中有更多详细信息。

#2 比较合适的比较


从技术上讲,您可以使用.toBe来验证任何值。 使用Jest,您可以专门使用某些比较工具来使测试更具可读性(在某些情况下更短)。

 // expect([1,2,3].length).toBe(3) // expect([1,2,3]).toHaveLength(3) const canBeUndefined = foo() // expect(typeof canBeUndefined !== 'undefined').toBe(true) // expect(typeof canBeUndefined).not.toBe('undefined') // expect(canBeUndefined).not.toBe(undefined) // expect(canBeUndefined).toBeDefined() class Foo { constructor(param) { this.param = param } // expect(new Foo('bar') instanceof Foo).toBe(true) // expect(new Foo('bar')).toBeInstanceOf(Foo) 

这些只是我从文档的Jest编译器的长列表中选择的一部分,您可以自己看看其余部分。

#3 在没有用户界面的情况下对元素进行快照测试


您可能已经在Jest中听说过快照测试 ,它可以帮助您跟踪用户界面元素中的更改。 但是,使用快照进行测试不限于此。

考虑以下示例:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees[0].nextMonthPaycheck).toBe(1000) expect(happyEmployees[1].nextMonthPaycheck).toBe(5000) expect(happyEmployees[2].nextMonthPaycheck).toBe(4000) // ...etc 

如果您要求越来越多的员工,那会很累。 另外,如果事实证明每位员工需要更多的索赔,则将新索赔的数量乘以员工的数量,您将获得一个想法。
通过快照测试,所有这些都可以像这样简单地完成:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees).toMatchSnapshot() 

每当发生回归时,您将确切知道节点中的哪棵树与图像不匹配。

但是便利是要付出代价的:这种方法更容易出错。 您可能不知道图片实际上是不正确的,最后无论如何您都会将其拍摄。 因此,请仔细检查您的快照,就像它是您自己的批准代码一样(因为是)。

当然,测试不仅限于快照。 阅读完整的文档

#4 描述和测试


您是否编写过与此相似的测试?

 describe('When I am a supervisor', () => { test('I should have a supervisor badge', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.badges).toContain('badge-supervisor') }) test('I should have a supervisor level', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.level).toBe('supervisor') }) }) describe('When I am a manager', () => { test('I should have a manager badge', () => { const employee = new Employee({ level: 'manager' }) expect(employee.badges).toContain('badge-manager') }) test('I should have a manager level', () => { const employee = new Employee({ level: 'manager' }) expect(employee.level).toBe('manager') }) }) 

这是单调和常规的,对吗? 想象在很多情况下都这样做。
使用description.eachtest.each可以按以下方式压缩代码:

 const levels = [['manager'], ['supervisor']] const privileges = [['badges', 'toContain', 'badge-'], ['level', 'toBe', '']] describe.each(levels)('When I am a %s', (level) => { test.each(privileges)(`I should have a ${level} %s`, (kind, assert, prefix) => { const employee = new Employee({ level }) expect(employee[kind])[assert](`${prefix}${level}`) }) }) 

但是,我还没有在自己的测试中使用它,因为我希望对测试进行详细说明,但我只是认为这将是一个有趣的技巧。

请参阅文档以获取有关参数的更多详细信息(扰流器:表语法确实很酷)。

#5 单一模仿全局功能


在某个时候,您将不得不在特定的测试用例中测试一些依赖于全局函数的东西。 例如,一个使用Date javascript对象接收有关当前日期的信息的函数,或一个依赖于此的库。 困难在于,涉及到当前日期时,您永远无法获得正确的陈述。

 function foo () { return Date.now() } expect(foo()).toBe(Date.now()) // This would throw occasionally: // expect(received).toBe(expected) // Object.is equality // // Expected: 1558881400838 // Received: 1558881400837 


最后,您将不得不重新定义全局Date对象,以使其一致且可管理:

 function foo () { return Date.now() } Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // 

但是,这被认为是不好的做法,因为在测试之间保持重新定义。 如果没有基于Date.now的其他测试,您将不会注意到这一点,但是它也会泄漏。

 test('First test', () => { function foo () { return Date.now() Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // ??? }) 

我曾经“破解”它,以便它不会泄漏:

 test('First test', () => { function foo () { return Date.now() const oriDateNow = Date.now Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // Date.now = oriDateNow }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

但是,有一种更好,更少黑客的方法来做到这一点:

 test('First test', () => { function foo () { return Date.now() jest.spyOn(Date, 'now').mockImplementationOnce(() => 1234567890123) expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

因此, jest.spyOn跟随全局Date对象,它仅模仿一次调用就模仿了now函数的实现。 反过来,其余的测试将使Date.now保持不变。

绝对有关于Jest中的存根的更多信息。 有关更多详细信息,请参见完整文档

这篇文章已经足够长了,所以我认为目前为止。 这仅影响Jest功能的一小部分,我只介绍我的最爱。 如果您还有其他有趣的事实,请告诉我。

另外,如果您经常使用Jest,请查看Majestic ,它是Jest的无GUI GUI,是无聊的终端输出的一种很好的替代方法。 我不确定作者是否从事开发,但仍然尊重这个人。

与往常一样,感谢您的关注!

仅此而已。 在课程中见。

Source: https://habr.com/ru/post/zh-CN457616/


All Articles