使用Mock Service Worker

使用Mock Service Worker

目錄

1. 引言

隨著前端應(yīng)用程序的復(fù)雜性不斷增加熙尉,開發(fā)者在開發(fā)和測試階段需要頻繁與后端 API 進(jìn)行交互。然而,在實(shí)際的開發(fā)過程中逆济,后端服務(wù)可能并不總是處于可用狀態(tài)扔罪,或者其開發(fā)進(jìn)度與前端不同步搁嗓。這種情況可能會(huì)導(dǎo)致前端開發(fā)進(jìn)度的滯后贬丛。為了應(yīng)對(duì)這一情況扇救,模擬 API 請(qǐng)求的工具應(yīng)運(yùn)而生志珍。

Mock Service Worker (MSW) 是一種基于 Service Worker API 的強(qiáng)大工具橙垢,專門用于在前端應(yīng)用程序中攔截和模擬網(wǎng)絡(luò)請(qǐng)求。通過使用 MSW伦糯,開發(fā)者可以輕松地模擬后端服務(wù)的各種行為柜某,例如響應(yīng)延遲錯(cuò)誤狀態(tài)碼等敛纲,確保在不依賴真實(shí)后端服務(wù)的情況下喂击,前端開發(fā)和測試工作能夠順利進(jìn)行。

2. 簡介

什么是 MSW

Mock Service Worker (MSW) 是一個(gè)用于攔截和模擬網(wǎng)絡(luò)請(qǐng)求的 JavaScript 庫淤翔。MSW 通過利用瀏覽器中的 Service Worker API 攔截應(yīng)用程序的 HTTP 請(qǐng)求翰绊,并根據(jù)預(yù)先定義的規(guī)則返回模擬響應(yīng)。這種方法不僅可以在開發(fā)過程中提高工作效率,還可以在測試中確保前端代碼的穩(wěn)定性和可靠性监嗜。

注意:Service Worker 只能在瀏覽器環(huán)境中工作琳要。在 Node.js 環(huán)境中,MSW 利用 Node.js 的請(qǐng)求攔截器庫秤茅,并允許重用來自瀏覽器環(huán)境的相同模擬定義稚补。

核心功能

  • 攔截并模擬 HTTP 請(qǐng)求:MSW 可以捕獲應(yīng)用程序發(fā)出的所有 HTTP 請(qǐng)求,并根據(jù)開發(fā)者定義的處理程序返回自定義的響應(yīng)框喳。
  • 支持 REST 和 GraphQL API:無論是傳統(tǒng)的 RESTful 接口還是更現(xiàn)代的 GraphQL 查詢课幕,MSW 都能夠輕松支持并模擬這些請(qǐng)求。
  • 無縫集成到前端開發(fā)和測試流程中:MSW 兼容各種前端開發(fā)工具和測試框架五垮,確保在各個(gè)階段都能方便地使用它進(jìn)行模擬乍惊。

3. 為什么選擇 MSW

在現(xiàn)代前端開發(fā)中,選擇適當(dāng)?shù)墓ぞ邅砟M API 請(qǐng)求是確保開發(fā)流程順暢的關(guān)鍵放仗。與傳統(tǒng)的 mock 工具相比润绎,MSW 具有以下顯著優(yōu)勢:

  • 與代碼無關(guān):MSW 不依賴于具體的代碼實(shí)現(xiàn),它在瀏覽器的網(wǎng)絡(luò)層攔截請(qǐng)求诞挨。這意味著你可以在不修改應(yīng)用邏輯的情況下使用 MSW莉撇。
  • 支持多種環(huán)境:MSW 可以在開發(fā)環(huán)境、測試環(huán)境惶傻,甚至生產(chǎn)環(huán)境中使用棍郎,幫助模擬不同場景下的 API 行為,確保環(huán)境的一致性银室。
  • 動(dòng)態(tài)響應(yīng):MSW 允許開發(fā)者根據(jù)不同的請(qǐng)求動(dòng)態(tài)生成響應(yīng)涂佃,從而使測試更加靈活和全面。
  • 對(duì)現(xiàn)代開發(fā)工具的良好兼容:MSW 支持 Service Worker API蜈敢,并與現(xiàn)代 JavaScript 模塊系統(tǒng)和工具鏈(如 Webpack辜荠、Rollup、Vite 等)良好集成抓狭。

與類似工具的比較

1 雖然可以處理 GraphQL 請(qǐng)求伯病,但需要額外設(shè)置,并非一流支持辐宾。
2 JSON Server 本身不在瀏覽器中運(yùn)行狱从,但可以從瀏覽器中請(qǐng)求。

工作原理

Mock Service Worker (MSW) 是一種用于模擬前端應(yīng)用網(wǎng)絡(luò)請(qǐng)求的工具叠纹,基于瀏覽器的 Service Worker API。其工作原理如下:

  1. 注冊(cè) Service Worker:應(yīng)用首先注冊(cè)一個(gè) Service Worker敞葛,它作為應(yīng)用與網(wǎng)絡(luò)之間的代理誉察,攔截所有 HTTP/HTTPS 請(qǐng)求。

  2. 請(qǐng)求攔截:當(dāng)應(yīng)用發(fā)出網(wǎng)絡(luò)請(qǐng)求時(shí)惹谐,Service Worker 可以捕獲并攔截這些請(qǐng)求持偏。

  3. 模擬響應(yīng):MSW 使用開發(fā)者定義的處理器驼卖,根據(jù)請(qǐng)求生成相應(yīng)的模擬響應(yīng)。

  4. 返回模擬響應(yīng):生成的響應(yīng)會(huì)返回給應(yīng)用程序鸿秆,就像來自真實(shí)服務(wù)器一樣酌畜。

    Node.js 支持:MSW 也可以在 Node環(huán)境中攔截并模擬網(wǎng)絡(luò)請(qǐng)求,適用于服務(wù)器端渲染和測試卿叽。

總結(jié)來說桥胞,MSW 的工作原理是通過瀏覽器的 Service Worker API 在網(wǎng)絡(luò)請(qǐng)求級(jí)別進(jìn)行攔截和模擬。這種方法使得模擬的過程與應(yīng)用程序代碼完全解耦考婴,并且允許開發(fā)者控制網(wǎng)絡(luò)行為贩虾,從而提高了前端開發(fā)和測試的靈活性和效率。

4. 實(shí)踐 MSW

安裝和配置

要開始使用 Mock Service Worker沥阱,首先需要在項(xiàng)目中安裝它缎罢。你可以通過 npm 或 yarn 安裝:

msw有1.x版本和2.x版本,如果項(xiàng)目node版本 < 18考杉,則需要使用1.x版本策精,本例子演示1.x版本的使用

npm install msw@1.3.2 --save-dev

或者

yarn add msw@1.3.2 --dev

安裝完成后,需要初始化 MSW 并創(chuàng)建一個(gè) Service Worker 文件來操作客戶端負(fù)責(zé)請(qǐng)求攔截崇棠。然而蛮寂,我們不必自己編寫任何worker的代碼,而是復(fù)制庫分發(fā)的worker文件易茬。 Mock Service Worker 提供了專用的 CLI 來幫助我們做到這一點(diǎn)酬蹋。

npx msw init public/ --save

然后在項(xiàng)目的src目錄下創(chuàng)建一個(gè)mocks 目錄,并在其中創(chuàng)建一個(gè)名為 handlers.ts 的文件來定義模擬的路由和響應(yīng)邏輯抽莱。

// src/mocks/handlers.ts
import { rest } from 'msw';

export const handlers = [
  rest.get('/api/user', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ username: 'John Doe' })
    );
  }),
];

接下來桅滋,在同一目錄下創(chuàng)建一個(gè)名為 browser.ts 的文件來設(shè)置并啟動(dòng) Service Worker。

// src/mocks/browser.ts
import { setupWorker } from 'msw';
import { handlers } from './handlers';

// 使用上面定義的處理器初始化 MSW Service Worker
export const worker = setupWorker(...handlers);

集成 MSW 到開發(fā)環(huán)境中

為了在開發(fā)環(huán)境中使用 MSW琉苇,你需要在應(yīng)用程序的入口文件(如 main.ts)中啟動(dòng)它狰腌。你可以根據(jù)當(dāng)前的環(huán)境條件來決定是否啟動(dòng) MSW,例如在開發(fā)環(huán)境中啟動(dòng)虐呻,在生產(chǎn)環(huán)境中則不啟動(dòng):

// src/main.ts
import { worker } from './mocks/browser'

// ...
  if (process.env.NODE_ENV === 'development') {
    worker.start()
  }
// ...

setupApp()

MSW 的一個(gè)重要特點(diǎn)是在開發(fā)過程中象泵,它可以用于模擬后端 API。開發(fā)者可以使用 MSW 攔截應(yīng)用中的所有 HTTP 請(qǐng)求斟叼,并根據(jù)需要調(diào)整 API 的響應(yīng)內(nèi)容偶惠,例如模擬延遲返回不同的 HTTP 狀態(tài)碼等朗涩。

接下來在handlers.ts 中編寫接口攔截的示例忽孽,模擬用戶的基本登錄流程:

// src/mocks/handlers.ts
import { rest } from 'msw'

export const handlers = [
  rest.post('/login', (req, res, ctx) => {
    // Persist user's authentication in the session
    sessionStorage.setItem('is-authenticated', 'true')

    return res(
      // Respond with a 200 status code
      ctx.status(200)
    )
  }),

  rest.get('/user', (req, res, ctx) => {
    // Check if the user is authenticated in this session
    const isAuthenticated = sessionStorage.getItem('is-authenticated')

    if (!isAuthenticated) {
      // If not authenticated, respond with a 403 error
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage: 'Not authorized'
        })
      )
    }

    // If authenticated, return a mocked user details
    return res(
      ctx.status(200),
      ctx.json({
        username: 'admin'
      })
    )
  })
]

使用技巧

隨著項(xiàng)目的復(fù)雜性增加,你可能會(huì)遇到一些更復(fù)雜的請(qǐng)求場景。這時(shí)兄一,MSW 的高級(jí)功能將派上用場厘线。例如,你可以:

  • 處理復(fù)雜的請(qǐng)求場景:使用 MSW出革,你可以精確控制請(qǐng)求的攔截和響應(yīng)邏輯造壮,處理諸如多種查詢參數(shù)、不同路徑參數(shù)等復(fù)雜場景骂束。
  • 模擬不同狀態(tài)碼和錯(cuò)誤響應(yīng):通過 MSW耳璧,模擬各種 HTTP 狀態(tài)碼(如 404、500 等)非常容易栖雾,你可以測試前端在遇到這些錯(cuò)誤時(shí)的處理方式楞抡。
  • 利用 MSW 進(jìn)行負(fù)載測試和壓力測試:雖然 MSW 主要用于開發(fā)和測試階段的功能測試,但你也可以用它進(jìn)行初步的負(fù)載測試析藕,評(píng)估前端應(yīng)用在高并發(fā)請(qǐng)求下的表現(xiàn)召廷。
  • 模擬延遲或文件上傳
// src/mocks/handlers.ts
import { rest } from 'msw';

export const handlers = [
  rest.get('/api/user/:userId', (req, res, ctx) => {
    const { userId } = req.params;
    return res(
      ctx.status(200),
      ctx.json({ username: `User ${userId}` })
    );
  }),
  rest.post('/api/login', (req, res, ctx) => {
    const { username, password } = req.body;
    if (username === 'admin' && password === 'admin') {
      return res(ctx.status(200), ctx.json({ token: 'abc123' }));
    }
    return res(ctx.status(403), ctx.json({ error: 'Invalid credentials' }));
  }),
];

而MSW能處理的場景遠(yuǎn)不僅如此,更多的功能可以移步官網(wǎng)查看手冊(cè)账胧;

v2版本:https://mswjs.io/docs/

v1 版本:https://v1.mswjs.io/docs/

5.在Node中實(shí)踐

BFF作為一個(gè)中間層項(xiàng)目竞慢,會(huì)經(jīng)常遇到調(diào)用其他接口的情況,例如獲取oss配置治泥,做統(tǒng)一鑒權(quán)等筹煮,接下來演示在BFF中的實(shí)踐,為2.x版本的使用:

添加基本配置

創(chuàng)建src/mocks 文件夾居夹,新增src/mocks/handlers/upload.handlers.ts 和 src/mocks/node.ts 文件:

// src/mocks/handlers/upload.handlers.ts 
import { http, HttpResponse } from 'msw';

interface RequestBody {
  key: string;
}

export const uploadHandlers = [
  // generate_security_token
  http.post("http://rccfile.dev1.rccchina.com/inner/generate_security_token", async ({ request, params }) => {
    const requestBody = await request.json();
    const { key: tokenKey } = requestBody as RequestBody;

    if (!tokenKey) {
      return HttpResponse.json({
        message: "Bad Request",
        code: 400,
        error: "Token key is required.",
      }, { status: 400 });
    }

    if (tokenKey === "empty_response") {
      return HttpResponse.json(null, { status: 200 });
    }

    if (tokenKey === "missing_fields") {
      return HttpResponse.json({
        message: "ok",
        code: 200,
        data: {
          data: {
            AccessKeyId: "mockAccessKeyId",
            SecurityToken: "mockSecurityToken",
            Bucket: "mockBucket",
            Path: "/mock/path",
          }
        },
      }, { status: 200 });
    }

    return HttpResponse.json({
      message: "ok",
      code: 200,
      data: {
        RegionId: "mock-region-id",
        AccessKeyId: "mock-access-key-id",
        AccessKeySecret: "mock-access-key-secret",
        SecurityToken: "mock-security-token",
        Bucket: "mock-bucket",
        Path: "/mock/path",
      },
    }, { status: 200 });
  }),
];


將 upload.handlers.ts 導(dǎo)入到 handlers.ts

// src/mock/handlers.ts
import { uploadHandlers } from './handlers/upload.handlers';

export const handlers = [
  ...uploadHandlers,
  // 將其他模塊的處理程序添加到此處
];

在node.ts中啟動(dòng)服務(wù)

// src/mocks/node.ts
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
 
export const server = setupServer(...handlers)

export const enableMocking = async () => {
  if (process.env.ENV !== "development_mock") {
    return;
  }
  return server.listen()
}

在入口文件注冊(cè)mock服務(wù)

// main.ts
import { enableMocking } from './mocks/node'

async function bootstrap() {
  // ...
  await enableMocking();
  // ... 
  await app.listen(serverConfig['port'], '0.0.0.0');
}
bootstrap();

通過控制不同的key傳入败潦,可以達(dá)到不同的接口模擬效果;

6. 總結(jié)

Mock Service Worker (MSW) 是現(xiàn)代前端開發(fā)者手中的一把利器准脂。它不僅可以在開發(fā)過程中為你模擬后端 API劫扒,從而擺脫對(duì)后端服務(wù)的依賴,還可以幫助你在測試中創(chuàng)建一致且可預(yù)測的測試環(huán)境狸膏。MSW 的靈活性和強(qiáng)大功能使得它在前端開發(fā)流程中的地位越來越重要沟饥。

通過使用 MSW,前端開發(fā)者可以更從容地面對(duì)開發(fā)和測試中的各種挑戰(zhàn)湾戳,不再受制于后端服務(wù)的限制贤旷,最終提升了開發(fā)效率和代碼質(zhì)量。無論是初學(xué)者還是經(jīng)驗(yàn)豐富的開發(fā)者砾脑,MSW 都是一款值得深入學(xué)習(xí)和掌握的工具幼驶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拦止,隨后出現(xiàn)的幾起案子县遣,更是在濱河造成了極大的恐慌糜颠,老刑警劉巖汹族,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萧求,死亡現(xiàn)場離奇詭異,居然都是意外死亡顶瞒,警方通過查閱死者的電腦和手機(jī)夸政,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榴徐,“玉大人守问,你說我怎么就攤上這事】幼剩” “怎么了耗帕?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長袱贮。 經(jīng)常有香客問我仿便,道長,這世上最難降的妖魔是什么攒巍? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任嗽仪,我火速辦了婚禮,結(jié)果婚禮上柒莉,老公的妹妹穿的比我還像新娘闻坚。我一直安慰自己,他們只是感情好兢孝,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布窿凤。 她就那樣靜靜地躺著,像睡著了一般跨蟹。 火紅的嫁衣襯著肌膚如雪雳殊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天喷市,我揣著相機(jī)與錄音相种,去河邊找鬼。 笑死品姓,一個(gè)胖子當(dāng)著我的面吹牛寝并,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腹备,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼衬潦,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了植酥?” 一聲冷哼從身側(cè)響起镀岛,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤弦牡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后漂羊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驾锰,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年走越,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了椭豫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旨指,死狀恐怖赏酥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谆构,我是刑警寧澤裸扶,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站搬素,受9級(jí)特大地震影響呵晨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔗蹋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一何荚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猪杭,春花似錦餐塘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蜂筹,卻和暖如春需纳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艺挪。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工不翩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人麻裳。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓口蝠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親津坑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妙蔗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361