纯Typescript测试相关配置
1: 安装jest相关的依赖
yarn add --dev jest @types/jest ts-jest
jest -- 我们的test runner, 测试断言库, mock库
@types/jest -- Jest的Typescript版
ts-jest -- 把Typescript编译为JavaScript
2:创建jest.config.js
在项目根目录下创建一个jest.config.js文件作为jest的配置文件,且添加以下内容
// jest.config.js
module.exports = {
roots: ['<rootDir>/src'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '^.+\\.test\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
3: 添加跑测试的脚本
// package.json
"scripts": {
"test": "jest",
//....
},
完成以上相关配置,我们就可以对一个Typescript纯函数进行测试了,例如以下例子:
//stringProcessor.ts
function reverseString(str: string) {
return str.split('').reverse().join('');
}
export {reverseString}
//stringProcessor.test.ts
import {reverseString} from './stringProcessor';
test('', ()=> {
expect(reverseString('hello')).toBe('olleh');
});
但是,如果我们需要测一个React component测试,我们需要借助另一个库Enzyme以及相关其他依赖。接下来,让我们来完成这部分的配置:
React测试相关配置
安装React测试相关的依赖
yarn add --dev @testing-library/react @testing-library/react-hooks @testing-library/jest-dom
@testing-library/react 测试React Component的库
@testing-library/react-hooks 测试自己写的的React Hooks的库
@testing-library/jest-dom 提供更多利于dom测试的断言
往jest.config.js文件里面添加支持React component测试的相关配置
//jest.config.js
module.exports = {
roots: ['<rootDir>/src'],
setupFiles: ['<rootDir>/test.setup.js', '<rootDir>/test.shim.js'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
'\\.(css|scss)': 'identity-obj-proxy', // mock 在react组件里import的CSS
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/testMocks/assetsMocks.js', //mock 在react组件里import的图片
},
testRegex: '^.+\\.test\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
在项目根目录添加testMocks/assetsMocks.js
//testMocks/assetsMocks.js
module.exports = '';
测试一个最简单的React组件
组件文件counter.tsx
//counter.tsx
import * as React from 'react';
import { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<div data-testid='count-announcement'> you have been clicked {count} times</div>
<button
data-testid='increase-button'
onClick={() => {
setCount(count + 1);
}}
>
{' '}
increase count
</button>
</div>
);
};
export default Counter;
测试文件counter.test.tsx
//counter.test.tsx
import * as React from 'react';
import Counter from './counter';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
test('the count should be 1 when you click the increase button once', () => {
// render
const { getByTestId } = render(<Counter />);
const increaseButton = getByTestId('increase-button');
// act
fireEvent.click(increaseButton);
// assert
expect(getByTestId('count-announcement')).toHaveTextContent('1');
});
测试一个有<Link>的React组件
组件文件HomePage.tsx
//HomePage.tsx
import React from 'react';
import logo from './images/logo.svg';
import pink from './images/pink.png';
import './HomePage.scss';
import { Link } from 'react-router-dom';
function App() {
const name = `nana`;
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<img src={pink} className='' alt='pink' />
<Link to='/blogList'>Blog List Page</Link>
</header>
<p data-testid='name'>name: {name}</p>
</div>
);
}
export default App;
测试文件HomePage.test.tsx
//HomePage.test.tsx
import * as React from 'react';
import HomePage from './HomePage';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { BrowserRouter } from 'react-router-dom';
//This is testing demo to testing a react component with CSS and Images import
test('should render name correctly', () => {
// render
const { getByTestId } = render(
//对于有Link的组件,在测试的时候,必须把它包到BrowserRouter里面
<BrowserRouter>
<HomePage />
</BrowserRouter>,
);
const name = getByTestId('name');
// act
// assert
expect(name).toHaveTextContent('nana');
});
这里需要特别注意的是: 对于有Link的组件,在测试的时候,必须把它包到BrowserRouter里面,不然JEST会不过。
测试一个有axios请求的组件
组件文件profile.tsx
//profile.tsx
import * as React from 'react';
import axios from 'axios';
import { useEffect, useState } from 'react';
const Profile = () => {
const [isLoading, setIsLoading] = useState(true);
const [username, setUsername] = useState('');
const [errorMessage, setErrorMessage] = useState('');
useEffect(() => {
async function fetchData() {
try {
const response = await axios.get('https://my-json-server.typicode.com/pengmq/mock-server/profile', {});
setIsLoading(false);
setUsername(response.data.data.username);
} catch (error) {
setIsLoading(false);
setErrorMessage(error);
}
}
fetchData();
}, []);
return (
<div>
<div data-testid='error-message'>{errorMessage}</div>
<div data-testid='loading-text'>
<span>{isLoading ? 'loading....' : ''}</span>
</div>
<div data-testid='username'>{username}</div>
</div>
);
};
export default Profile;
测试文件profile.test.tsx
//profile.test.tsx
import * as React from 'react';
import { render, waitForElement } from '@testing-library/react';
import Profile from './profle';
import axios from 'axios';
//jest.mock(...)自动mock axios的API调用,这行代码必须放在文件最外层,不能放到test里面
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>; //This line is needed when work with typescript
test('loading text should hide and user name should show after get profile data successfully from server ', async () => {
//mock axios的API返回,这行代码必须在render之前
mockedAxios.get.mockResolvedValueOnce({ data: { data: { username: 'nana' } } });
const { getByTestId, container } = render(<Profile />);
// awaiting for sync function to be done with await waitForElement()
const [loadingText, username] = await waitForElement(() => [getByTestId('loading-text'), getByTestId('username')], {
container,
});
//assert dom changes
expect(loadingText).toHaveTextContent('');
expect(username).toHaveTextContent('nana');
});
网友评论