乐趣区

关于javascript:前端单元测试jest入门实践

前言

单元测试是用来测试程序中一小块性能的,比如说一个函数、一个类。它能很显著地进步我的项目的代码品质,升高呈现 Bug 的频率,并且也利于保护代码。

话虽如此,然而绝大多数人是不违心去写单测的。。不过不影响,多学点不是好事。

背景就不多做介绍,次要是实际。

注释

本次我的项目中,我用的框架是 Jest。用法比较简单,这里简略介绍一下:

什么是单元测试?

单元测试(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性测验的测试工作。

TDD: 测试驱动开发

先编写测试用例,在测试用例的领导上来欠缺性能,当测试用例编写完并且都通过测试之后,相应的性能也就做完了。例如函数和组件库。但通常在代码发生变化的时候,测试用例也要进行相应的调整。

BDD: 行为驱动开发

测试用例模仿用户的操作行为,通常在实现业务代码开发之后,以用户的操作为领导编写测试代码。当测试用例跑通之后,就能够认为零碎的整体流程曾经晦涩。BDD 的模式实用于平时的业务代码开发,因为业务的需要有可能变更频繁,但操作流程有可能不会变动,当业务代码发生变化的时候,能够应用原来的测试用例持续跑代码,节俭了开发工夫。
TDD 负责办法类、独立组件的测试。BDD 则负责整体业务模块的测试。

为什么须要单测?

现状:
不足意识 :没有单测意识,很多代码没有单测
不足设计 :模块不足设计,互相耦合,写单测艰难
测试艰难 :降级公共 API,人工测试验证艰难
测试不全面:局部代码分支泛滥,人工测试没方法笼罩所有分支

怎么掂量单测欠缺度?

单测欠缺水平的掂量用覆盖率来掂量

单测的意义

能力建设 :一个具备开发教训的开发人员,基本上都会编写单元测试。即使不会,能够通过培训来疾速达成。从学习曲线上看,单元测试很容易上手。
晋升效率 :可能通过 mock 数据,及早发现问题,而越早发现 Bug,造成的节约就会越小。
谋求卓越 :单元测试能够充当一个设计工具,它有助于开发人员去思考代码构造的设计,让代码更加有利于测试。
测试更全面 :可能笼罩 QA 测试笼罩不到的状况,比方各种 if 分支、异样解决。
更有信念:降级公共 API 时,如果依赖这个 API 的所有代码单测都能通过,那咱们对这次代码降级是更有信念的。

理念介绍到此结束,接下来是相干实际

1. 断言

在 Jest 中应用最多的就是断言库,咱们能够用它来测试指标函数的输入是否与预期统一。

test("测试 2 + 2 = 4",()=>{expect(sum(2,2)).toBe(4)
})
test("测试函数返回对象为 {name:'zhi'}",()=>{expect(getName()).toEqual({name:"zhi"})
})

更多能够查看官网文档。

2. 异步代码测试

通常会有两种写法:

  • 回调函数
  • 函数返回 promise

在测试异步代码的时候,通常返回的数据是不确定的,因而咱们只须要测试异步代码是否失常返回数据即可。

对于回调函数,如果像同步函数一样测试,是没有方法获取正确的断言后果

export const funcA = (callback: (data: number) => void): void => {setTimeout(() => {callback(1);
  }, 1000);
};
​
test('funcA', () => {funcA((data) => expect(data).toEqual(2));
});
​

即便是这样,咱们的单测也能够通过。

这是因为 jest 在运行完 funcA 后就间接完结了,不会期待 setTimeout 的回调,天然也就没有执行 expect 断言。正确的做法是,传入一个 done 参数:

test('funcA', (done) => {funcA((data) => {expect(data).toEqual(2);
    done();});
});

在回调执行完之后显式地通知 jest 异步函数执行结束,jest 会等到执行了 done() 之后再完结,这样就能失去预期的后果了。

对于 promise,只须要在 test 用例完结时,把 Promise 返回即可。

export const funcB = (): Promise<number> => {return new Promise<number>((resolve) => {setTimeout(() => {resolve(1);
    }, 1000);
  });
};
​
test('funcB', () => {return funcB().then((data) =>   expect(data).toEqual(1));
});
// 也能够应用 await
test('funcB', async () => {const data = await funcB();
  expect(data).toEqual(1);
});

对于 promise 抛出异样,则须要应用 .catch 办法。这里有个须要留神的中央。增加 expect.assertions 来验证肯定数量的断言被调用。否则,一个 fulfilled 状态的 Promise 不会让测试用例失败。

test('the fetch fails with an error', () => {expect.assertionsassertions(1);
  return fetchData().catch(e => expect(e).toMatch('error'));
});

3. Mock

如果咱们须要测试回调函数是否执行,能够用 mock。在这里想基于实际介绍一下 mock 的用法。

场景 1:一个 react hooks 应用了 react-router 的 useLocation。咱们想模仿这个有没有调用

import {renderHook, act} from '@testing-library/react-hooks';

let mockLocation = jest.fn(() => {
    return {search: "test"}
})

jest.mock("react-router-dom", () => {
    return {useLocation: () => mockLocation()}
})

// 应用

test("useTabChange", () => {const { result, rerender} = renderHook(() => useTabChange());
    expect(mockLocation).toBeCalled()})

咱们甚至能够给任何办法外部的函数模仿返回

例子 2: 在业务中用到了一个 ’opa-fe-base’ 的库,咱们须要模仿一下返回

import {renderHook, act} from '@testing-library/react-hooks';
let mockFn = jest.fn()
jest.mock("opa-fe-base", () => ({storeAPI: { getEntity: () => mockFn()} }))
import useCountry from '../useCountry'
​
describe("useCountry", () => {test("useCountry to return SG", () => {const { result, rerender} = renderHook(() => useCountry())
        expect(result.current).toBe('SG')
        mockFn = jest.fn(() => {return { country: "ID"}
        })
        rerender()
        act(() => {expect(result.current).toBe('ID')
        })
      
    })
})

上述例子能够看到,咱们模仿了 ’opa-fe-base’ 这个库返回了一个蕴含

storeAPI的对象,对象外部还有个 mock 的办法。咱们能够通过扭转 mock 办法的返回来判断返回是否达到预期。

留神:须要测试的函数 import 写在咱们 mock 的上面

最近在写单测的时候发现 window.URL.createObjectURL 事件能够间接应用 jest.fn 进行模仿。然而 document 的事件却模仿不胜利,查问文档发现能够应用 jest.spyOn。

场景 3:通过 jest.spyOn 模仿 document 事件

const spyFn = jest.spyOn(document, 'createElement')
const mockRevoke = jest.fn();
​
describe("downloadBlobFile", () => {beforeEach(() => {window.URL.createObjectURL = jest.fn((blob) => {return blob})
        window.URL.revokeObjectURL = mockRevoke;
    })
    test("downloadBlobFile must be called", () => {downloadBlobFile({});
        expect(spyFn).toBeCalled()
        expect(mockRevoke).toBeCalled();})
​
    test("download createObjectUrl use", () => {downloadBlobFile("test");
        expect(window.URL.createObjectURL.mock.calls[0][0]).toEqual('test')
    })
})

4. 快照

帮忙咱们确保在保护代码的过程中不会对组件的 UI 进行扭转。

expect(组件实例).toMatchSnapshot()

在第一次执行时会生成快照,之后就会去比照每次的快照是否一样。

5. 举荐一个 vs code jest 插件

Jest Runner

这个插件能够在咱们的 test 文件中渲染出按钮,帮忙咱们独自的运行或者调试某一个 test 或者 describe。不须要全局运行。晋升效率。

最初

单测的编写是有意义的,然而往往在工作中囿于业务,没有机会写,且行且珍惜~~

退出移动版