Thử nghiệm mock testing với Mock Service Worker

Với tính đơn giản khi thực hiện và cơ chế hoạt động độc đáo với cách can thiệp vào tầng network bằng Service Worker, Mock Service Worker là một công cụ mô phỏng API đáng để chúng ta cân nhắc sử dụng.

Upload image

Trong quá trình phát triển phần mềm, các module thường được phát triển độc lập và không phải phụ thuộc vào nhau. Để quá trình tích hợp được diễn ra suôn sẻ, ta có thể sử dụng testing server hay mock service để mô phỏng việc tương tác với các module phụ thuộc. Việc xây dựng testing server mặc dù đem lại tính thực tế khi tương tác, nhưng lại khiến việc kiểm thử trở nên phức tạp khi phải phụ thuộc vào testing server. Một cách đơn giản, nhanh chóng và độc lập, ta có thể sử dụng mock service để mô phỏng module cần tương tác.

Hiện nay có khá nhiều thư viện hỗ trợ mock testing để chúng ta sử dụng, điển hình như Mock Service Worker (MSW), Nock hay Mocha,… Mỗi thư viện đều có cơ chế vận hành và ưu nhược điểm khác nhau để chúng ta lựa chọn. Trong bài viết này, ta hãy cùng nhau thử nghiệm và tìm hiểu về MSW bằng cách thử sử dụng nó trong NuxtJS nhé.

Thử nghiệm Mock Service Worker

Khởi tạo project

Việc đầu tiên chúng ta cần làm là khởi tạo một project cho NuxtJS.

npx nuxi@latest init nuxt-with-msw

Sau đó cài đặt bộ thư viện MSW.

cd nuxt-with-msw
yarn add --dev msw

Khởi tạo mock service logic

Ta sẽ tiến hành khởi tạo file handler chứa các đoạn xử lý cho từng đầu API cần mô phỏng.

// mocks/handlers.js
import { rest } from 'msw'

const baseUrl = process.env.SERVER_HOST || '/'

// Định nghĩa các handler cho từng endpoint
export const handlers = [
  // Endpoint 1
  rest.get(`${baseUrl}api/me`, (req, res, ctx) => { 
    return res(
      ctx.status(200),
      ctx.json({
        name: 'Atekco',
      }),
    )
  }),
  // Endpoint 2
  rest.get(`${baseUrl}api/posts`, (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({
        posts: [
          'Bun: Công cụ all-in-one siêu nhanh và đa năng cho ứng dụng JavaScript',
          'Top 4 con át chủ bài của Trung Quốc trong cuộc cạnh tranh mô hình ngôn ngữ lớn thế giới',
          'Văn hóa chia sẻ kiến thức trong tổ chức kĩ sư',
        ],
      }),
    )
  }),
]

Việc tạo ra các đầu API khá đơn giản, chúng ta chỉ cần định nghĩa method, endpoint và tạo hàm xử lý cho từng đầu API.

Mock Service Worker hỗ trợ chúng ta ở cả hai môi trường là node và browser, trong ví dụ này, ta sẽ tạo file browser để thử nghiệm.

// mocks/browser.js

import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)

Thêm service worker

Tiếp theo, chúng ta sẽ đưa file service worker của MSW vào trong project trong thư mục “public” (nơi chứa các file tài nguyên của web) tên thư mục này có thể khác ở những ngôn ngữ khác nhau. Ngoài cách lấy trực tiếp file để đem vào trong project thì MSW cũng hỗ trợ chúng ta CLI để tự động khởi tạo service worker.

npx msw init public/ --save

Trong câu lệnh, ta truyền đường dẫn của thư mục public vào. Với option “—save” thì MSW sẽ có thể tự động cập nhật service worker file khi ta nâng cấp thư viện MSW trong tương lai.

Chạy service worker trong project

Việc cuối cùng ta cần config là chạy mocking service khi trang web được tải. Để thực hiện điều này trong Nuxt, chúng ta sẽ khởi tạo một plugin ở client side.

// plugins/msw.client.js
import { worker } from '@/mocks/browser'

export default defineNuxtPlugin(_ => {
  // Chỉ chạy ở môi trường development
  if (process.env.NODE_ENV === 'development') {
    worker.start({
      // Mặc định thì MSW sẽ log một dòng warning khi truy cập một file chưa có 
      onUnhandledRequest: 'bypass',
    })
  }
})

Cuối cùng là khởi tạo một trang web để tương tác với các đầu API vừa tạo.

<template>
  <div>
    <h1>Welcome, {{ name }}</h1>
    <button @click="fetchPosts">Get Latest Posts</button>
    <ul>
      <li v-for="post in posts" :key="post">
        {{ post }}
      </li>
    </ul>
  </div>
</template>

<script setup>
const name = ref('')
const posts = ref([])

const fetchUserInfo = async function () {
  const res = await fetch('/api/.auth/me')
  const info = await res.json()
  name.value = info.name
}

async function fetchPosts() {
  const res = await fetch('/api/posts')
  const data = await res.json()
  posts.value = data.posts
}

onMounted(async () => {
  await fetchUserInfo()
})
</script>

Kết quả

Vậy là mọi thứ đã xong, khởi động project và xem thành quả thôi nào.

yarn dev --env NODE_ENV=development

Upload image

Service worker đã được khởi động

Upload image

Kiểm tra status code của API trong tab network

Những đặc điểm nổi bật

Gọn nhẹ và dễ sử dụng

Như đã thấy ở phần trải nghiệm, quá trình đặt MSW vào trong project là cực kì dễ dàng và gọn nhẹ khi chỉ cần cài đặt một thư viện MSW duy nhất và khởi tạo file worker chỉ tầm 8kb. Phần code logic của handlers nằm tách biệt khỏi code project, giúp việc quản lý trở nên độc lập và hoàn toàn có thể được sử dụng lại ở nhiều project khác nhau.

Do các file handler và worker nằm hoàn toàn ở client side, MSW hỗ trợ rất tốt quá trình phát triển phần mềm khi ta có thể thoải mái sửa đổi handlers để kiểm thử hành vi của trang web mà không cần phải khởi động lại dev server.

Sử dụng service worker để chặn và giả lập API

Mock Service Worker sử dụng service workers, thứ hoạt động như một tầng trung gian giữa ứng dụng web và browser, nên có thể can thiệp vào request/response một cách dễ dàng mà không cần phải quan tâm việc ta dùng phương pháp nào để gọi API, từ Axios, fetch hay XMLHttpRequest.

Do can thiệp ở tầng network, ta có thể giả lập bất cứ API nào mà không cần quan tâm tới tên miền của dường dẫn đó. Khi gặp một đường dẫn không được giả lập, service worker sẽ gọi API đó và trả về kết quả thật cho chúng ta.

Upload image

Service worker sẽ gọi và chuyển tiếp response

Đối với môi trường node, do không có sự tồn tại của service worker, nên MSW sẽ sử dụng phương pháp sửa đổi các module giúp kết nối đến internet. Đó là lý do tại sao MSW cung cấp hai hàm là “setupWorker” cho browser và “setupServer” cho node. Và đừng lo, cả hai hàm này đều sử dụng chung logic mocking nên ta không cần phải lo lắng về việc viết lại.

Hỗ trợ GraphQL

Ngoài việc hỗ trợ giả lập cho REST API, MSW cũng hỗ trợ chúng ta giả lập GraphQL API, thứ ít được hỗ trợ trong các thư viện khác.

Tổng kết

Với hơn 13 nghìn lượt star trên Github, Mock Service Worker là một thư viện đáng tận dụng nhờ vào sự tách biệt và tính đơn giản khi sử dụng. Ta có thể dùng MSW trong những ứng dụng từ nhỏ nhẹ đến to lớn và phức tạp, bên cạnh đó, nó còn có thể được sử dụng trong cả quá trình debugging và testing.

MSW cũng có hệ thống document rất tốt giúp việc tiếp cận và nắm được cách vận hành trở nên nhanh chóng. Đồng thời, MSW cũng tự so sánh với các thư viện tương tự khác trên document để chúng ta có thể cân nhắc sử dụng.

Trên đây là bài viết giới thiệu về Mock Service Worker của mình, mong là nó sẽ giúp ích cho các bạn trong việc hiểu và sử dụng thư viện trên khi cần.

Atekco - Home for Authentic Technical Consultants