Jest单元测试最佳实践1:使用eslint-plugin-jest规范测试代码

Jest单元测试最佳实践1:使用eslint-plugin-jest规范测试代码

项目中的荒草地

我们经常只关注业务代码的代码质量,而忽略了测试用例的代码质量。这让单元测试代码狂野生长。从而导致单元测试代码往往不起作用。这也是有些程序员认为单元测试代码没什么用。所以我会分享我在项目中发现的jest单元测试最佳实践。

从代码风格开始

让我们从测试代码的风格开始吧。我们一般都会用eslint来规范业务代码。你是否尝试过使用eslint来规范单元测试代码呢?试试看 eslint-plugin-jest 吧。这是这个项目的页面https://www.npmjs.com/package/eslint-plugin-jest
如果访问速度太慢,可以使用淘宝NPM镜像。

这些是我在项目中使用的规则列表

'jest/expect-expect': 'error',
'jest/lowercase-name': [
    'error',
    {
        ignore: ['describe'],
    },
],
'jest/no-disabled-tests': 'error'
'jest/no-done-callback': 'error',
'jest/no-duplicate-hooks': 'error',
'jest/no-conditional-expect': 'error',
'jest/no-export': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/no-interpolation-in-snapshots': 'error',
'jest/no-jasmine-globals': 'error',
'jest/no-jest-import': 'error',
'jest/no-large-snapshots': 'error',
'jest/no-mocks-import': 'error',
'jest/no-standalone-expect': 'error',
'jest/no-test-prefixes': 'error',
'jest/valid-describe': 'error',
'jest/valid-expect-in-promise': 'error',
'jest/prefer-to-have-length': 'warn',
'jest/valid-expect': 'error',

大部分规则从名字上都很容易理解。我想重点介绍其中几个规则

jest/no-done-callback

如果你喜欢用done 来测试异步代码。你可能要改变你的代码习惯了。不要再用done了。因为一旦代码有什么错误造成无法执行到done所在的代码。单元测试不会失败,而是会超时。而且使用回调函数来调用done也让代码晦涩难懂。

以下是我们可能会使用done的场景

测试异步操作

如果你要测试异步操作。请返回Promise对象。比如

return doSomething().then(data => {...})

如果你可以用async...await 那么就使用它。async...await 让代码更容易理解。

测试setTimeout

如果你想测试 setTimeout. 你可以在单元测试文件的开头使用 jest.useFakeTimers() 然后在你调用了业务代码后调用 jest.runAllTimers()jest.runAllTimers可以快速的跳过你设置的等待时间。比如

doOperationAfterTime();
jest.runAllTimers();
expect(1).toBe(1);

如果你想了解 timer mocker 的更多用法,可以参考 https://jestjs.io/docs/timer-mocks.

jest/no-conditional-expect

在条件代码中使用 expect 可能会导致 expect 被无声无息的跳过。把 expect 放到 catch 同样不是个好主意。这同样会导致expect 被跳过。

以下是坏的例子

it ('foo', () => {
    const result = doSomething();
    if (result === 1) {
        expect(1).toBe(1)
    }
})

it ('bar', () => {
    try {
        await foo();
    } catch (err) {
        expect(err).toMatchObject({ code: 'MODULE_NOT_FOUND' });
    }
})

这些是好的例子

it ('foo', () => {
    const result = doSomething();
    expect(result).toBe(1);
    expect(1).toBe(1);
})

it ('throws an error', () => {
    await expect(foo).rejects.toThrow(Error);
})

jest/no-identical-title

no-identical-title 是个简单但是非常有用的规则。它的作用是防止两个测试用例使用同样的名字

以下是坏的例子

it('should do bar', () => {});
it('should do bar', () => {}); // Has the same title as the previous test

我曾经不止一次有过这样的经验。当单元测试失败的时候,我花了很久的时间调试,但是它们依然失败。最后我才意识到我调试的单元测试并不是失败的那个。这很让人沮丧,尤其是当两个有着相同名字的测试用例都失败的时候。

这是我的这篇博文的英文版:https://dev.to/alexxiyang/jest-best-practice-1-use-eslint-plugin-jest-o7e

上一篇:jest


下一篇:web前端单元测试入门,以Ant Design Pro为例