<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前端項(xiàng)目中寫單元測(cè)試其實(shí)很簡單

          共 13763字,需瀏覽 28分鐘

           ·

          2024-05-24 08:50

             

          大廠技術(shù)  高級(jí)前端  Node進(jìn)階

          點(diǎn)擊上方 程序員成長指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群

            本文轉(zhuǎn)載于UCloud云通信技術(shù)團(tuán)隊(duì)

          一、關(guān)于自動(dòng)化測(cè)試

          1、測(cè)試分類

          自動(dòng)化測(cè)試類型常分為以下三種,各有優(yōu)缺點(diǎn):

          • 單元測(cè)試(Unit Test)

            • 對(duì)項(xiàng)目中低耦合的工具類庫和公共子組件進(jìn)行測(cè)試,較為簡單,能在一定程度上保障代碼質(zhì)量
          • 集成測(cè)試(Integration Test)

            • 對(duì)于耦合度較高的函數(shù)/組件對(duì)外暴露的接口進(jìn)行測(cè)試,能較大程度保障產(chǎn)品質(zhì)量,但開發(fā)成本高
          • UI測(cè)試(UI Test)

            • 前端中UI變動(dòng)大,適合人工檢查

          了解測(cè)試術(shù)語

          • 1、TDD(測(cè)試驅(qū)動(dòng)開發(fā))
          • 2、BDD(行為驅(qū)動(dòng)開發(fā))
          • 3、測(cè)試覆蓋率
          • 4、快照測(cè)試
          • 5、模擬函數(shù)
          • 6、斷言

          2、單元測(cè)試框架

          • Jest[1]:是一個(gè)廣受歡迎的單元測(cè)試框架,簡單易用,功能強(qiáng)大。
          • Vitest[2]:它由 Vue / Vite 團(tuán)隊(duì)成員開發(fā)和維護(hù),在 Vite 的項(xiàng)目集成它會(huì)非常簡單,而且速度非常快。
          • Mocha:一個(gè)靈活的測(cè)試框架,需要各種插件來配合使用。
          • Karma:能在真實(shí)的瀏覽器中測(cè)試,可配置其他單元測(cè)試框架
          • Jasmine:功能全面的測(cè)試框架,相對(duì)復(fù)雜、不夠靈活

          測(cè)試框架太多,且各有優(yōu)勢(shì),大多數(shù)寫法相差不多。

          我們這里選擇Jest來分享,其他測(cè)試框架可以自行了解。

          3、單元測(cè)試適用的測(cè)試對(duì)象有哪些?

          • 1、常見工具類函數(shù)
          • 2、公共子組件
          • 3、接口請(qǐng)求數(shù)據(jù)

          二、給項(xiàng)目配置Jest

          1、安裝

          yarn add -D jestnpm install jest -D

          2、配置(非必需)

          如果你想獲得更多的jest配置,可以增加配置文件。

          比如項(xiàng)目中的Jest,默認(rèn)不顯示測(cè)試覆蓋率和測(cè)試報(bào)告等,想要支持,就需要我們將Jest的配置文件暴露出來,只需要執(zhí)行yarn test --initnpx jest --init

          然后根據(jù)需求選擇對(duì)應(yīng)的配置,最后會(huì)在根目錄下生成jest.config.js文件

          image.png

          根據(jù)提示選擇即可,這里我們選擇JsDom環(huán)境,需要代碼測(cè)試覆蓋率報(bào)告,自動(dòng)清除每個(gè)單元測(cè)試之間的模擬調(diào)用和實(shí)例。

          執(zhí)行完成后,發(fā)現(xiàn)在項(xiàng)目根目錄下多了一個(gè)jest.config.js文件,里面包含了各種配置說明

          module.exports = { 
              // 是否顯示覆蓋率報(bào)告 
              collectCoveragetrue
              // 告訴 jest 文件測(cè)試要求的閾值,單位為百分比
              // coverageThreshold: {
              //   global: {
              //      statements: 90, // 每行
              //        functions: 80, // 每個(gè)函數(shù)
              //        branches: 90 // 分支覆蓋率
              //    }
              // }
          }

          此時(shí)再次執(zhí)行單元測(cè)試,發(fā)現(xiàn)顯示了測(cè)試覆蓋率

          image.png

          用瀏覽器打開coverage目錄下的index.html,可以看到此時(shí)頁面顯示了測(cè)試報(bào)告

          image.png

          3、配置快速執(zhí)行命令

          package.json

          {
            "scripts": {
              "start""node index.js",
              "test""jest",
              "coverage""jest --coverage"
            }
          }

          執(zhí)行命令啟動(dòng)單元測(cè)試yarn testyarn coverage

          4、項(xiàng)目配置

          一般通過腳手架生成的項(xiàng)目,已經(jīng)默認(rèn)配置了測(cè)試框架,比如React的項(xiàng)目配置了JestVue3.x項(xiàng)目默認(rèn)配置了Vitest

          • Jest默認(rèn)支持Commonjs

            • 如果你的項(xiàng)目不支持ESM,需要安裝@babel/core@babel/preset-env進(jìn)行轉(zhuǎn)譯。
            • 如果你的項(xiàng)目需要支持TS,可以@types/jest@babel/preset-typescript

          執(zhí)行yarn test發(fā)現(xiàn)報(bào)錯(cuò),是因?yàn)樾枰渲?code style="color: rgb(30, 107, 184);font-size: 14px;line-height: 1.8em;letter-spacing: 0em;background: none 0% 0% / auto no-repeat scroll padding-box border-box rgba(27, 31, 35, 0.05);width: auto;height: auto;margin-left: 2px;margin-right: 2px;padding: 2px 4px;border-style: none;border-width: 3px;border-color: rgb(0, 0, 0) rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4);border-radius: 4px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">babel

          image.png

          配置babel

          安裝插件,在根目錄下新建.babelrc文件

          // .babelrc
          {
              "plugins": [
                 [
                  "@babel/plugin-syntax-jsx"
                 ]
               ],
               "presets": [ "@babel/preset-env""@babel/preset-react" ]
          }

          注意

          • 如何支持或忽略.css文件
          • 如何忽略單行、函數(shù)或文件、目錄

          5、快速上手單元測(cè)試

          比如我們有sum.js

          export function sum(a, b){
              return a + b;
          }
          export function mins(a, b){
              return a - b;
          }

          為這個(gè)函數(shù)寫測(cè)試文件

          import { sum, mins } from './sum'
          it(`should add 1 + 2 to equal 3`, () => {
              expect(sum(12)).toBe(3);
          });
          test(`mins 2 - 1 to equal 1`, () => {
              expect(min(21)).toBe(1);
          })

          入門很簡單,只需要針對(duì)每個(gè)函數(shù)做一些預(yù)期的校驗(yàn)即可,當(dāng)不小心改動(dòng)了源代碼導(dǎo)致輸出的結(jié)果和預(yù)期不符,將會(huì)測(cè)試不通過,這樣就保證了代碼功能的穩(wěn)定。

          describe、test預(yù)留字段基本沒有區(qū)別,描述方式不同,一個(gè)it should,另一個(gè)test action

          三、項(xiàng)目中如何開始寫單元測(cè)試

          寫單元測(cè)試要考慮清楚幾點(diǎn):

          • 測(cè)試的主要目的不是證明代碼的正確,而是為了發(fā)現(xiàn)錯(cuò)誤。
          • 測(cè)試代碼,只考慮外部接口,不考慮內(nèi)部實(shí)現(xiàn)
          • 充分考慮數(shù)據(jù)的邊界條件
          • 對(duì)重點(diǎn)、核心代碼重點(diǎn)測(cè)試
          • 減少測(cè)試代碼數(shù)量,避免無用功
          • 基于需求寫單元測(cè)試

          1、在項(xiàng)目根目錄下新建tests目錄,將單元測(cè)試文件放在其中,測(cè)試文件命名xx.test.js,優(yōu)點(diǎn)是可以更好的管理測(cè)試文件,缺點(diǎn)是不好找到源文件

          2、在對(duì)應(yīng)文件的目錄下新建__test__目錄,測(cè)試文件放置其中,優(yōu)點(diǎn)就是容易找到執(zhí)行文件,但不容易過濾和管理

          1、給工具函數(shù)寫單元測(cè)試

          給工具函數(shù)寫測(cè)試函數(shù)是單元測(cè)試很重要的一個(gè)場(chǎng)景,我們以金額千分位格式化處理函數(shù)為例,通過單元測(cè)試發(fā)現(xiàn)問題。

          // 將數(shù)字千分位格式化后返回對(duì)應(yīng)的字符串
          export function getThousandFormatNum(num{
              const str = num + '';
              const reg = str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+.)/g : /(\d{1,3})(?=(\d{3})+(?:$|.))/g;
              return str.replace(reg, '$1,');
          }

          單元測(cè)試

          import { getThousandFormatNum } from './common'

          describe('getThousandFormatNum', () => {
              // 常規(guī)數(shù)字格式化
              it('should return a string with thousand format', () => {
                expect(getThousandFormatNum(1000)).toBe('1,000');
                expect(getThousandFormatNum(1000000)).toBe('1,000,000');
                expect(getThousandFormatNum(123456789)).toBe('123,456,789');
              });
              
               // 格式化后和本身相同的數(shù)字
              it('should return the same number if it is not greater than 999', () => {
                expect(getThousandFormatNum(0)).toBe('0');
                expect(getThousandFormatNum(999)).toBe('999');
              });
              
              // 格式化負(fù)數(shù)
              it('should handle negative numbers correctly', () => {
                expect(getThousandFormatNum(-1000)).toBe('-1,000');
                expect(getThousandFormatNum(-1000000)).toBe('-1,000,000');
                expect(getThousandFormatNum(-123456789)).toBe('-123,456,789');
              });
              // 格式化帶小數(shù)的數(shù)字
              it('should handle decimal numbers correctly', () => {
                expect(getThousandFormatNum(1234.56)).toBe('1,234.56');
                expect(getThousandFormatNum(1234567.89)).toBe('1,234,567.89');
              });
          });

          執(zhí)行單元測(cè)試

          image.png

          2、給組件寫單元測(cè)試(快照測(cè)試)

          前端主要就是組件,但業(yè)務(wù)組件變動(dòng)比較頻繁,所以傾向于給公共組件或組件庫增加單元測(cè)試,防止組件擴(kuò)展或變更導(dǎo)致業(yè)務(wù)Bug。

          我們以APP.js組件為例,寫單元測(cè)試,并生成快照。

          function App({
            return (
              <div className="App">
                  <HashRouter basename="/">
                    <div style={{marginBottom: 20}}>
                      <Link style={{marginRight: 20}} to="/">Home更新版本1</Link>
                      <Link to="/about">About更新版本123</Link>
                    </div>
                    <Suspense fallback={<div>Loading...</div>}>
                      <Routes>
                        <Route path="/" element={<AFunction />}></Route>
                        <Route path="/about" element={<BFunction />} />
                      </Routes>
                    </Suspense>
                  </HashRouter>
              </div>

            );
          }

          export default App;

          App.js寫單元測(cè)試

          import { render, screen, act } from '@testing-library/react';
          import App from './App';

          test('renders learn react link'async () => {
            let tree;
            await act(async () => {
              tree = render(<App />);
            })
            const linkElement = screen.getByText(/About更新版本123/i);
            expect(linkElement).toBeInTheDocument();
            expect(tree).toMatchSnapshot();
          });

          當(dāng)我們改動(dòng)App.js,單元測(cè)試發(fā)現(xiàn)上個(gè)版本的快照更新了,就會(huì)報(bào)錯(cuò),提醒檢查,如果更改沒問題,可以執(zhí)行u更新快照

          image.png

          3、模擬函數(shù)(Mock)

          Mock是單元測(cè)試中很重要的一部分,他一般在下面場(chǎng)景中使用

          • 模擬數(shù)據(jù)
          • 模擬接口請(qǐng)求
          • 模擬定時(shí)器,比如setTimout 1小時(shí),那每次測(cè)試花費(fèi)一小時(shí)就瘋了
          • 組件使用Redux怎么測(cè)試

          在組件中,經(jīng)常有一些引用的變量

          const mock = jest.fn();  
          mock.mockReturnValue(42);  
          mock(); // 42  
            
          mock.mockReturnValue(43);  
          mock(); // 43

          模擬接口請(qǐng)求

          test('async test'async () => {  
              const asyncMock = jest.fn().mockResolvedValue(43);  // Promise
              await asyncMock(); // 43  
          });

          模擬函數(shù)有很多,在實(shí)際使用過程中需要各種結(jié)合使用,這里僅展示了最簡單的使用。

          4、常用斷言方法

          在工具函數(shù)測(cè)試過程中,我們常常要判斷變量類型和值,測(cè)試框架往往提供了判斷方法,下面是Jest一些常見的判斷,更多可以查閱官網(wǎng)[3]

          toBe:判斷測(cè)試結(jié)果為某個(gè)值

          not:否定判斷

          test('the best flavor is not coconut', () => {  
              expect(bestLaCroixFlavor()).toBe('coconut');  
          });
          test('the best flavor is not coconut', () => {  
              expect(bestLaCroixFlavor()).not.toBe('coconut');  
          });

          toEqual:檢測(cè)引用類型,遞歸檢查屬性和屬性值

          toEqual會(huì)調(diào)用Object.is方法,toBe ===

          const can1 = {  
              flavor'grapefruit',  
              ounces12,  
          };  
          const can2 = {  
              flavor'grapefruit',  
              ounces12,  
          };  
            
          describe('the La Croix cans on my desk', () => {  
              test('have all the same properties', () => {  
                  expect(can1).toEqual(can2);  // true
              });  
              test('are not the exact same can', () => {  
                  expect(can1).not.toBe(can2);  // true
              });  
          });

          toMatch:匹配字符串規(guī)則,正則匹配

          describe('an essay on the best flavor', () => {  
              test('mentions grapefruit', () => {  
                  expect(essayOnTheBestFlavor()).toMatch(/grapefruit/); 
                  expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));  
              });  
          });

          toBeTruthy:匹配if條件為真

          drinkSomeLaCroix();  
          if (thirstInfo()) {  
              drinkMoreLaCroix();  
          }

          四、查看單元測(cè)試的結(jié)果

          單元測(cè)試完成后,執(zhí)行測(cè)試命令

          yarn testnpx jest

          1、測(cè)試覆蓋率解讀

          image.png
          • Stmts (Statements):語句覆蓋率,即被測(cè)試覆蓋的代碼語句的百分比。在你的代碼中,92.85% 的語句被測(cè)試覆蓋。
          • Branch:分支覆蓋率,即被測(cè)試覆蓋的條件分支的百分比。在你的代碼中,100% 的分支被測(cè)試覆蓋。
          • Funcs (Functions):函數(shù)覆蓋率,即被測(cè)試覆蓋的函數(shù)的百分比。在你的代碼中,83.33% 的函數(shù)被測(cè)試覆蓋。
          • Lines:行覆蓋率,即被測(cè)試覆蓋的代碼行數(shù)的百分比。在你的代碼中,100% 的行被測(cè)試覆蓋。
          • Uncovered Line:未覆蓋的行號(hào)。這一列列出了未被測(cè)試覆蓋的代碼行的行號(hào)范圍。

          2、測(cè)試信息解讀

          image.png
          • Test Suites: 2 passed, 2 total:這表示你有 2 個(gè)測(cè)試套件,其中所有的 2 個(gè)測(cè)試套件都通過了。
          • Tests: 8 passed, 8 total:這表示你一共運(yùn)行了 8 個(gè)測(cè)試,其中所有的 8 個(gè)測(cè)試都通過了。
          • Snapshots: 1 total:這表示1個(gè)快照測(cè)試(Snapshot Testing)。
          • Time: 2.703 s:這表示測(cè)試運(yùn)行的時(shí)間為 2.703 s 秒。

          3、參考

          • Jest官網(wǎng)[4]
          • Jest實(shí)踐指南[5]

          Node 社群

               


          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對(duì)Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。

             “分享、點(diǎn)贊在看” 支持一下

          瀏覽 91
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          1點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  猫咪AV成人永久网站在线观看 | 欧美成人在线三级免费 | 丁香五月资源 | 黄色视频在线观看地址 | 狠狠干 |