前端测试策略别让你的代码在上线后崩溃毒舌时刻这代码写得跟定时炸弹似的不知道什么时候就炸了。各位前端同行咱们今天聊聊前端测试策略。别告诉我你还在手动测试那感觉就像在没有安全网的情况下走钢丝——能走但随时可能掉下去。为什么你需要测试策略最近看到一个项目上线后发现一个严重 bug导致用户无法登录。我就想问你是在做产品还是在做实验反面教材// 反面教材没有测试 function Calculator() { const [result, setResult] React.useState(0); const [num1, setNum1] React.useState(); const [num2, setNum2] React.useState(); const add () { setResult(parseInt(num1) parseInt(num2)); }; const subtract () { setResult(parseInt(num1) - parseInt(num2)); }; return ( div input typetext value{num1} onChange{(e) setNum1(e.target.value)} / input typetext value{num2} onChange{(e) setNum2(e.target.value)} / button onClick{add}/button button onClick{subtract}-/button div结果: {result}/div /div ); } export default Calculator;毒舌点评这代码就像在黑暗中开车出了事故都不知道怎么回事。正确姿势1. 单元测试// 正确姿势单元测试 // 1. 安装依赖 // npm install --save-dev jest testing-library/react testing-library/jest-dom // 2. 配置 // jest.config.js module.exports { testEnvironment: jsdom, setupFilesAfterEnv: [rootDir/src/setupTests.js], }; // src/setupTests.js import testing-library/jest-dom; // 3. 测试组件 // __tests__/Calculator.test.js import React from react; import { render, fireEvent, screen } from testing-library/react; import Calculator from ../Calculator; test(renders calculator components, () { render(Calculator /); expect(screen.getByText(结果: 0)).toBeInTheDocument(); }); test(adds two numbers correctly, () { render(Calculator /); const num1Input screen.getByDisplayValue(); const num2Input screen.getByDisplayValue(); const addButton screen.getByText(); fireEvent.change(num1Input, { target: { value: 10 } }); fireEvent.change(num2Input, { target: { value: 5 } }); fireEvent.click(addButton); expect(screen.getByText(结果: 15)).toBeInTheDocument(); }); test(subtracts two numbers correctly, () { render(Calculator /); const num1Input screen.getByDisplayValue(); const num2Input screen.getByDisplayValue(); const subtractButton screen.getByText(-); fireEvent.change(num1Input, { target: { value: 10 } }); fireEvent.change(num2Input, { target: { value: 5 } }); fireEvent.click(subtractButton); expect(screen.getByText(结果: 5)).toBeInTheDocument(); });2. 集成测试// 正确姿势集成测试 // 1. 安装依赖 // npm install --save-dev testing-library/react-hooks // 2. 测试自定义 hook // __tests__/useCounter.test.js import { renderHook, act } from testing-library/react-hooks; import useCounter from ../hooks/useCounter; test(should increment counter, () { const { result } renderHook(() useCounter()); act(() { result.current.increment(); }); expect(result.current.count).toBe(1); }); test(should decrement counter, () { const { result } renderHook(() useCounter()); act(() { result.current.decrement(); }); expect(result.current.count).toBe(-1); }); test(should reset counter, () { const { result } renderHook(() useCounter()); act(() { result.current.increment(); result.current.reset(); }); expect(result.current.count).toBe(0); });3. 端到端测试// 正确姿势端到端测试 // 1. 安装依赖 // npm install --save-dev playwright/test // 2. 配置 // playwright.config.js module.exports { use: { baseURL: http://localhost:3000, headless: true, }, }; // 3. 测试文件 // tests/example.spec.js const { test, expect } require(playwright/test); test(home page has title, async ({ page }) { await page.goto(/); await expect(page).toHaveTitle(/My App/); }); test(user can login, async ({ page }) { await page.goto(/login); await page.fill(input[nameusername], testuser); await page.fill(input[namepassword], password); await page.click(button[typesubmit]); await expect(page).toHaveURL(/dashboard); }); test(user can add a post, async ({ page }) { await page.goto(/login); await page.fill(input[nameusername], testuser); await page.fill(input[namepassword], password); await page.click(button[typesubmit]); await page.goto(/posts/new); await page.fill(input[nametitle], Test Post); await page.fill(textarea[namecontent], This is a test post); await page.click(button[typesubmit]); await expect(page).toHaveURL(/posts); await expect(page).toHaveText(Test Post); });4. 测试覆盖率// 正确姿势测试覆盖率 // 1. 配置 Jest 覆盖率 // jest.config.js module.exports { testEnvironment: jsdom, setupFilesAfterEnv: [rootDir/src/setupTests.js], collectCoverage: true, collectCoverageFrom: [ src/**/*.{js,jsx,ts,tsx}, !src/**/*.d.ts, ], }; // 2. 运行测试 // npm test -- --coverage // 3. 查看覆盖率报告 // 打开 coverage/lcov-report/index.html毒舌点评这才叫前端测试策略从单元测试到端到端测试全面覆盖再也不用担心线上崩溃了。
前端测试策略:别让你的代码在上线后崩溃
前端测试策略别让你的代码在上线后崩溃毒舌时刻这代码写得跟定时炸弹似的不知道什么时候就炸了。各位前端同行咱们今天聊聊前端测试策略。别告诉我你还在手动测试那感觉就像在没有安全网的情况下走钢丝——能走但随时可能掉下去。为什么你需要测试策略最近看到一个项目上线后发现一个严重 bug导致用户无法登录。我就想问你是在做产品还是在做实验反面教材// 反面教材没有测试 function Calculator() { const [result, setResult] React.useState(0); const [num1, setNum1] React.useState(); const [num2, setNum2] React.useState(); const add () { setResult(parseInt(num1) parseInt(num2)); }; const subtract () { setResult(parseInt(num1) - parseInt(num2)); }; return ( div input typetext value{num1} onChange{(e) setNum1(e.target.value)} / input typetext value{num2} onChange{(e) setNum2(e.target.value)} / button onClick{add}/button button onClick{subtract}-/button div结果: {result}/div /div ); } export default Calculator;毒舌点评这代码就像在黑暗中开车出了事故都不知道怎么回事。正确姿势1. 单元测试// 正确姿势单元测试 // 1. 安装依赖 // npm install --save-dev jest testing-library/react testing-library/jest-dom // 2. 配置 // jest.config.js module.exports { testEnvironment: jsdom, setupFilesAfterEnv: [rootDir/src/setupTests.js], }; // src/setupTests.js import testing-library/jest-dom; // 3. 测试组件 // __tests__/Calculator.test.js import React from react; import { render, fireEvent, screen } from testing-library/react; import Calculator from ../Calculator; test(renders calculator components, () { render(Calculator /); expect(screen.getByText(结果: 0)).toBeInTheDocument(); }); test(adds two numbers correctly, () { render(Calculator /); const num1Input screen.getByDisplayValue(); const num2Input screen.getByDisplayValue(); const addButton screen.getByText(); fireEvent.change(num1Input, { target: { value: 10 } }); fireEvent.change(num2Input, { target: { value: 5 } }); fireEvent.click(addButton); expect(screen.getByText(结果: 15)).toBeInTheDocument(); }); test(subtracts two numbers correctly, () { render(Calculator /); const num1Input screen.getByDisplayValue(); const num2Input screen.getByDisplayValue(); const subtractButton screen.getByText(-); fireEvent.change(num1Input, { target: { value: 10 } }); fireEvent.change(num2Input, { target: { value: 5 } }); fireEvent.click(subtractButton); expect(screen.getByText(结果: 5)).toBeInTheDocument(); });2. 集成测试// 正确姿势集成测试 // 1. 安装依赖 // npm install --save-dev testing-library/react-hooks // 2. 测试自定义 hook // __tests__/useCounter.test.js import { renderHook, act } from testing-library/react-hooks; import useCounter from ../hooks/useCounter; test(should increment counter, () { const { result } renderHook(() useCounter()); act(() { result.current.increment(); }); expect(result.current.count).toBe(1); }); test(should decrement counter, () { const { result } renderHook(() useCounter()); act(() { result.current.decrement(); }); expect(result.current.count).toBe(-1); }); test(should reset counter, () { const { result } renderHook(() useCounter()); act(() { result.current.increment(); result.current.reset(); }); expect(result.current.count).toBe(0); });3. 端到端测试// 正确姿势端到端测试 // 1. 安装依赖 // npm install --save-dev playwright/test // 2. 配置 // playwright.config.js module.exports { use: { baseURL: http://localhost:3000, headless: true, }, }; // 3. 测试文件 // tests/example.spec.js const { test, expect } require(playwright/test); test(home page has title, async ({ page }) { await page.goto(/); await expect(page).toHaveTitle(/My App/); }); test(user can login, async ({ page }) { await page.goto(/login); await page.fill(input[nameusername], testuser); await page.fill(input[namepassword], password); await page.click(button[typesubmit]); await expect(page).toHaveURL(/dashboard); }); test(user can add a post, async ({ page }) { await page.goto(/login); await page.fill(input[nameusername], testuser); await page.fill(input[namepassword], password); await page.click(button[typesubmit]); await page.goto(/posts/new); await page.fill(input[nametitle], Test Post); await page.fill(textarea[namecontent], This is a test post); await page.click(button[typesubmit]); await expect(page).toHaveURL(/posts); await expect(page).toHaveText(Test Post); });4. 测试覆盖率// 正确姿势测试覆盖率 // 1. 配置 Jest 覆盖率 // jest.config.js module.exports { testEnvironment: jsdom, setupFilesAfterEnv: [rootDir/src/setupTests.js], collectCoverage: true, collectCoverageFrom: [ src/**/*.{js,jsx,ts,tsx}, !src/**/*.d.ts, ], }; // 2. 运行测试 // npm test -- --coverage // 3. 查看覆盖率报告 // 打开 coverage/lcov-report/index.html毒舌点评这才叫前端测试策略从单元测试到端到端测试全面覆盖再也不用担心线上崩溃了。