夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • 博客搭建

  • Ajax

  • Vite

  • Vitest

    • vitest单元测试
    • 处理异步测试
    • 测试示例
      • 普通js测试
      • 基于testing-library的dom操作测试
      • 组件测试
        • 组件
        • 测试用例
      • service mock
      • axios mock
  • Nuxt

  • UI库文章

  • Docker

  • 技术
  • Vitest
夜猫子
2024-04-17
目录

测试示例

# 使用 Vitest 进行项目测试

# 普通js测试

test.concurrent('handles js', () => {
  expect(1 + 1).toBe(2);
});
1
2
3

# 基于testing-library的dom操作测试

import '@testing-library/jest-dom';

describe('dom test', () => {
  test.concurrent('testing-library jest-dom', async () => {
    // 创建div,并设置id
    const div = document.createElement('div');
    div.id = 'adm-mask';
    
    // 此时div不为空
    expect(div).not.toBeNull();
    expect(div).toBeDefined();
    expect(div).toBeInstanceOf(HTMLDivElement);

    // 追加到body上
    await document.body.appendChild(div);
    const mask: HTMLElement = document.body.querySelector('#adm-mask');
    expect(mask).toBeInTheDocument();

    // 移除div
    div.remove();
    expect(mask).not.toBeInTheDocument();
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 组件测试

这里测的是 antd-mobile,必须以es的形式导入,否则test会报错

# 组件

import demo from '@/assets/demo.svg'; // 继承了vite的alias配置
import styles from './Test.module.scss'; // 使用模块化样式
import { CheckOutline } from 'antd-mobile-icons'; // 使用antd-mobile-icons
import { Loading } from 'antd-mobile/es'; // 使用antd-mobile
import './Demo.scss'; // 使用scss -> npm i sass
import './Demo.less'; // 使用less -> npm i less

export default function Demo() {
  return (
    <div className="demo a b">
      <p data-testid="demo" className={styles.test}>Hello demo</p>
      {/* 使用svg标签 */}
      <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <circle cx="100" cy="50" r="40" stroke="black" strokeWidth="2" fill="red" />
      </svg>
      {/* 使用svg图标 */}
      <img src={demo} alt="svg" />
      {/* 使用antd mobile的svg标签 */}
      <CheckOutline />
      {/* 使用antd mobile的组件 */}
      <Loading />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 测试用例

import Demo from '@/views/test-demo/Demo';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';

test('render component', async () => {
  const { unmount, getByText, findByTestId, container } = await render(<Demo />);

  expect(getByText('Hello demo')).not.toBeEmptyDOMElement();

  expect((await findByTestId('demo')).textContent).toEqual('Hello demo');

  unmount();

  expect(container).toBeEmptyDOMElement();
  expect(container.innerHTML).toBeFalsy();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# service mock

项目中会遇到对于接口请求的封装,比如将请求统一安排到XxxService.ts中 然后通过await XxxService.queryXxx(..); 去获取数据

  1. 在XxxService.ts的所在目录下创建__mocks__文件夹
  2. *touch mocks/*XxxService.ts
  3. 修改*mocks/*XxxService.ts
// 对,你没看错,就这样简单
import { vi } from 'vitest';

export default {
  updateAppointOrderState: vi.fn(() => Promise.resolve(false)),
  queryAllGuides: vi.fn(() => Promise.resolve({
    guiders: [{ name: 'jerryime' }],
    defaultGuider: { name: 'jerryime' },
  })),
}
1
2
3
4
5
6
7
8
9
10
// 测试用例方式1:(全局mock测试)

import { BookingService } from '@/services';
import { vi } from 'vitest';

vi.mock('@/services/BookingService'); // 全局mock

describe.skip('mock api', () => {
  it('mock async service in global scope', async () => {
    const result = false;
    const res = await BookingService.updateAppointOrderState(); // 不会走真实接口请求

    expect(res).toBe(result);
    expect(res).toEqual(result);
    expect(res).toBeFalsy();
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 测试用例方式2:(不使用全局mock)

import { BookingService } from '@/services';
import { vi } from 'vitest';

describe.skip('mock api', () => {
  it('mock async service', async () => {
    const response = true;
    // 手动模拟指定请求函数,返回一次mock数据
    // TIPS:该方式也可以覆盖测试用例方式1的数据
    const spy = vi.spyOn(BookingService, 'updateAppointOrderState').mockImplementation(() => Promise.resolve(response));

    const res = await BookingService.updateAppointOrderState(); // 不会走真实接口请求
    expect(res).toEqual(response);
    expect(spy).toBeCalledTimes(1);
    expect(spy.mock.results[0].value).toBe(response);
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# axios mock

  1. 项目root目录创建_mocks__文件夹
  2. touch _mocks__/axios.ts
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { vi } from 'vitest';

const getMockResponse = (config: AxiosRequestConfig, type = 'request') => {
  let response = null;
  const { url } = config;
  console.log(`${type} mock invoked url:: `, url);
  const res = {
    status: 200,
    statusText: 'OK',
    data: {
      code: 200,
      data: response,
    },
  } as AxiosResponse;
  // console.log('mock axios response:', res);
  return res;
};

const mockByRequest = vi.fn((config = {} as AxiosRequestConfig) => {
  const res = getMockResponse(config, 'request');
  return Promise.resolve(res);
});

const mockByGet = vi.fn((url = '', params = {}) => {
  const res = getMockResponse({ url, params, method: 'get' }, 'get');
  return Promise.resolve(res);
});

const mockByPost = vi.fn((url = '', params = {}) => {
  const res = getMockResponse({ url, params, method: 'post' }, 'post');
  return Promise.resolve(res);
});

const mockByPut = vi.fn((url = '', params = {}) => {
  const res = getMockResponse({ url, params, method: 'put' }, 'put');
  return Promise.resolve(res);
});

function Axios(config = {} as AxiosRequestConfig) {
  if (config.url) {
    return Axios.request(config);
  }
}

Axios.request = mockByRequest;
Axios.get = mockByGet;
Axios.post = mockByPost;
Axios.put = mockByPut;

export default Axios;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 测试用例

import axios from 'axios';
import { vi, expect, test, JestMockCompatFn, beforeEach } from 'vitest';

vi.mock('axios');

beforeEach(() => {
  vi.clearAllMocks();
});

test('axios test:: 全局mock', async () => {
  const result = await axios.get('/invalid-path');

  expect(axios.patch).toBeUndefined();
  expect(axios.get).toHaveBeenCalledWith('/invalid-path');
  expect(axios.get).toBeCalledTimes(1);
  expect(result).toMatchObject({
    status: 200,
    statusText: 'OK',
    data: { code: 200, data: null },
  });
});

test('axios test:: axios(config)调用', async () => {
  // axios(config)调用,内部走的是axios.request
  const result = await axios({ url: '/invalid-path', method: 'get' });

  expect(axios.request).toHaveBeenCalledWith('/invalid-path');
  expect(axios.request).toBeCalledTimes(1);
  expect(result).toMatchObject({
    status: 200,
    statusText: 'OK',
    data: { code: 200, data: null },
  });
});

test('axios test:: 手动mock', async () => {
  const defaultVal = 'hello';

  (axios.get as JestMockCompatFn<any[], any>).mockResolvedValueOnce(defaultVal);
  const result = await axios.get('/xxx');

  expect(axios.get).toHaveBeenCalledWith('/xxx');
  expect(axios.get).toBeCalledTimes(1);
  expect(result).toEqual(defaultVal);
});

test('can get actual axios', async () => {
  const ax = await vi.importActual<typeof axios>('axios');

  expect(vi.isMockFunction(ax.get)).toBe(false);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
编辑 (opens new window)
上次更新: 2024/4/18 10:11:43
处理异步测试
SSR

← 处理异步测试 SSR→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式