Appearance
Testing
Tại sao cần kiểm thử?
Kiểm thử tự động giúp bạn và đội của bạn xây dựng các ứng dụng Vue phức tạp một cách nhanh chóng và tự tin bằng cách ngăn chặn sự thoái hóa và khuyến khích bạn phân chia ứng dụng thành các hàm, module, lớp, và thành phần có thể kiểm thử được. Như bất kỳ ứng dụng nào khác, ứng dụng Vue mới của bạn cũng có thể gặp sự cố theo nhiều cách, và quan trọng là bạn có thể phát hiện và sửa chúng trước khi phát hành.
Trong hướng dẫn này, chúng ta sẽ đề cập đến các thuật ngữ cơ bản và đưa ra các khuyến nghị về công cụ nào nên chọn cho ứng dụng Vue 3 của bạn.
Có một phần đặc biệt cho Vue liên quan đến composables. Xem Kiểm thử Composables phía dưới để biết thêm chi tiết.
Khi nào nên kiểm thử
Bắt đầu kiểm thử sớm! Chúng tôi khuyến nghị bạn bắt đầu viết các bài kiểm thử ngay khi có thể. Càng chờ đến lúc thêm kiểm thử vào ứng dụng của bạn, ứng dụng của bạn sẽ có nhiều phụ thuộc hơn, và việc bắt đầu sẽ trở nên khó khăn hơn.
Các loại kiểm thử
Khi thiết kế chiến lược kiểm thử cho ứng dụng Vue của bạn, bạn nên tận dụng các loại kiểm thử sau:
- Unit (Đơn vị): Kiểm tra rằng đầu vào cho một hàm, lớp hoặc composables cụ thể đang tạo ra đầu ra hoặc hiệu ứng phụ như mong đợi.
- Component (Thành phần): Kiểm tra rằng thành phần của bạn có thể kết nối, hiển thị, có thể tương tác và hoạt động như mong đợi. Các kiểm thử này nhập nhiều mã hơn so với kiểm thử đơn vị, phức tạp hơn và yêu cầu nhiều thời gian hơn để thực hiện.
- End-to-end (Từ đầu đến cuối): Kiểm tra các tính năng mà bao gồm nhiều trang và thực hiện các yêu cầu mạng thực tế đối với ứng dụng Vue đã xây dựng ở môi trường sản xuất. Các kiểm thử này thường liên quan đến việc triển khai cơ sở dữ liệu hoặc backend khác.
Mỗi loại kiểm thử đều đóng một vai trò trong chiến lược kiểm thử của ứng dụng của bạn, và mỗi loại sẽ bảo vệ bạn khỏi các loại sự cố khác nhau.
Tổng quan
Chúng tôi sẽ tóm tắt ngắn gọn về từng loại kiểm thử, cách chúng có thể được triển khai cho các ứng dụng Vue và đưa ra một số khuyến nghị chung.
Kiểm thử Đơn vị
Kiểm thử đơn vị được viết để xác minh rằng các đơn vị nhỏ, cô lập của mã đang hoạt động như mong đợi. Một kiểm thử đơn vị thường bao gồm một hàm
, lớp, composables hoặc module duy nhất. Kiểm thử đơn vị tập trung vào tính đúng đắn logic và chỉ quan tâm đến một phần nhỏ của chức năng tổng thể của ứng dụng. Chúng có thể giả mạo một phần lớn môi trường của ứng dụng của bạn (ví dụ: trạng thái ban đầu, các lớp phức tạp, module của bên thứ ba và yêu cầu mạng).
Nói chung, kiểm thử đơn vị sẽ phát hiện các vấn đề với logic kinh doanh và tính chính xác logic.
Ví dụ, hãy xem xét hàm increment
này:
js
// helpers.js
export function increment(current, max = 10) {
if (current < max) {
return current + 1
}
return current
}
Vì nó rất tự đóng, sẽ dễ dàng gọi hàm increment và xác nhận rằng nó trả về những gì nó nên trả về, nên chúng ta sẽ viết một kiểm thử đơn vị.
Nếu bất kỳ khẳng định nào trong số này bị thất bại, rõ ràng vấn đề nằm trong hàm increment
.
js
// helpers.spec.js
import { increment } from './helpers'
describe('increment', () => {
test('increments the current number by 1', () => {
expect(increment(0, 10)).toBe(1)
})
test('does not increment the current number over the max', () => {
expect(increment(10, 10)).toBe(10)
})
test('has a default max of 10', () => {
expect(increment(10)).toBe(10)
})
})
Như đã đề cập trước đó, kiểm thử đơn vị thường được áp dụng cho logic kinh doanh tự đóng, thành phần, lớp, module hoặc hàm không liên quan đến việc hiển thị UI, yêu cầu mạng hoặc các vấn đề môi trường khác.
Có hai trường hợp khi BẠN kiểm thử đặc trưng của Vue:
- Composables
- Components
Composables
Một loại hàm cụ thể cho ứng dụng Vue là Composables, có thể đòi hỏi xử lý đặc biệt trong quá trình kiểm thử. Xem Kiểm thử Composables phía dưới để biết thêm chi tiết.
Kiểm thử đơn vị cho Components
Một thành phần có thể được kiểm thử theo hai cách:
Whitebox: Kiểm thử Đơn vị
Các kiểm thử "Whitebox tests" hiểu về chi tiết triển khai và các phụ thuộc của một thành phần. Chúng tập trung vào cô lập thành phần được kiểm thử. Những kiểm thử này thường bao gồm việc giả mạo một số, nếu không phải tất cả, các thành phần con của thành phần của bạn, cũng như thiết lập trạng thái và phụ thuộc của plugin (ví dụ: Pinia).
Blackbox: Kiểm thử Thành phần
Các kiểm thử "Blackbox tests" không biết về chi tiết triển khai của một thành phần. Chúng giả mạo ít nhất có thể để kiểm thử tích hợp của thành phần và toàn bộ hệ thống. Chúng thường render tất cả các thành phần con và được xem xét là "kiểm thử tích hợp" hơn. Xem Đề xuất kiểm thử thành phần phía dưới.
Khuyến nghị
Vì cài đặt chính thức được tạo bởi
create-vue
dựa trên Vite, chúng tôi khuyến nghị sử dụng một framework kiểm thử đơn vị có thể tận dụng cùng cấu hình và đường ống biến đổi trực tiếp từ Vite. Vitest là một framework kiểm thử đơn vị được thiết kế đặc biệt cho mục đích này, được tạo ra và duy trì bởi các thành viên của đội Vue / Vite. Nó tích hợp với các dự án dựa trên Vite một cách dễ dàng và nhanh chóng.
Các Lựa Chọn Khác
- Jest là một framework kiểm thử đơn vị phổ biến. Tuy nhiên, chúng tôi chỉ khuyến nghị Jest nếu bạn có một bộ kiểm thử Jest hiện tại cần được di chuyển sang một dự án dựa trên Vite, vì Vitest cung cấp một tích hợp mượt mà hơn và hiệu suất tốt hơn.
Kiểm Thử Thành Phần
Trong ứng dụng Vue, các thành phần là các khối xây dựng chính của giao diện người dùng. Do đó, các thành phần là đơn vị tự nhiên để phân đoạn khi đánh giá hành vi của ứng dụng của bạn. Từ góc độ chi tiết, kiểm thử thành phần nằm ở mức độ nằm giữa kiểm thử đơn vị và có thể xem xét là một dạng của kiểm thử tích hợp. Rất nhiều ứng dụng Vue của bạn nên được bao phủ bởi một kiểm thử thành phần và chúng tôi khuyến nghị mỗi thành phần Vue đều có một tệp mô tả của riêng mình.
Kiểm thử thành phần nên bao gồm các vấn đề liên quan đến props của thành phần, các sự kiện, khe cắm mà nó cung cấp, các kiểu, lớp, lifecycle hooks, và nhiều hơn nữa.
Kiểm thử thành phần không nên giả mạo các thành phần con, mà thay vào đó kiểm thử tương tác giữa thành phần của bạn và các thành phần con của nó bằng cách tương tác với chúng như một người dùng. Ví dụ, một kiểm thử thành phần nên click vào một phần tử giống như một người dùng thay vì tương tác với thành phần theo cách lập trình.
Kiểm thử thành phần nên tập trung vào giao diện công khai của thành phần thay vì chi tiết triển khai nội bộ. Đối với hầu hết các thành phần, giao diện công khai giới hạn ở: sự kiện phát ra, props và khe cắm. Khi kiểm thử, hãy nhớ kiểm thử điều mà thành phần làm, không phải làm thế nào nó làm điều đó.
LÀM ĐIỀU NÀY
Đối với Logic Hiển Thị: xác nhận đầu ra render chính xác dựa trên props và khe cắm được đưa vào.
Đối với Logic Hành Vi: xác nhận cập nhật render chính xác hoặc sự kiện được phát ra đúng khi có sự kiện nhập vào từ người dùng.
Trong ví dụ dưới đây, chúng ta thể hiện một thành phần Stepper có một phần tử DOM được đặt tên là "increment" và có thể được click. Chúng ta truyền vào một prop có tên
max
để ngăn Stepper tăng lên quá2
, vì vậy nếu chúng ta click vào nút 3 lần, giao diện người dùng vẫn nên hiển thị là2
.Chúng ta không biết gì về triển khai của Stepper, chỉ biết "đầu vào" là prop
max
và "đầu ra" là trạng thái của DOM như người dùng sẽ thấy nó.
Vue Test Utils
Cypress
Testing Library
js
const valueSelector = '[data-testid=stepper-value]'
const buttonSelector = '[data-testid=increment]'
const wrapper = mount(Stepper, {
props: {
max: 1
}
})
expect(wrapper.find(valueSelector).text()).toContain('0')
await wrapper.find(buttonSelector).trigger('click')
expect(wrapper.find(valueSelector).text()).toContain('1')
KHÔNG LÀM NHƯ VẬY
Không khẳng định trạng thái riêng tư của một thể hiện thành phần hoặc kiểm thử các phương thức riêng tư của một thành phần. Kiểm thử chi tiết triển khai làm cho bài kiểm thử dễ gãy, vì chúng có khả năng bị hỏng và yêu cầu cập nhật khi triển khai thay đổi.
Nhiệm vụ tối cung của thành phần là hiển thị đầu ra DOM đúng, vì vậy các bài kiểm thử tập trung vào đầu ra DOM cung cấp cùng mức độ đảm bảo độ chính xác (nếu không phải nhiều hơn) trong khi vẫn mạnh mẽ và chống lại sự thay đổi.
Không phụ thuộc hoàn toàn vào các bài kiểm thử snapshot. Khẳng định chuỗi HTML không mô tả tính chính xác. Viết bài kiểm thử có sự chủ ý.
Nếu một phương thức cần được kiểm thử kỹ lưỡng, xem xét trích nó thành một hàm tiện ích độc lập và viết một bài kiểm thử đơn riêng cho nó. Nếu nó không thể được trích xuất sạch sẽ, có thể kiểm thử nó như là một phần của một bài kiểm thử thành phần, tích hợp hoặc từ đầu đến cuối có nó.
Khuyến nghị
Vitest cho các thành phần hoặc composables render headlessly (ví dụ:
useFavicon
trong VueUse). Các thành phần và DOM có thể được kiểm thử bằng cách sử dụng@vue/test-utils
.Cypress Component Testing cho các thành phần mà hành vi mong đợi của nó phụ thuộc vào việc hiển thị kiểu đúng hoặc kích hoạt sự kiện DOM nguyên bản. Nó có thể được sử dụng với Testing Library thông qua @testing-library/cypress.
Sự khác biệt chính giữa Vitest và các trình chạy dựa trên trình duyệt là tốc độ và ngữ cảnh thực thi. Nói một cách ngắn gọn, các trình chạy dựa trên trình duyệt, như Cypress, có thể bắt được các vấn đề mà các trình chạy dựa trên nút, như Vitest, không thể bắt được (ví dụ: vấn đề kiểu, sự kiện DOM nguyên bản thực, cookie, lưu trữ cục bộ và sự cố mạng), nhưng trình chạy dựa trên trình duyệt chậm hơn nhiều lần so với Vitest vì chúng mở một trình duyệt, biên dịch bảng điều khiển của bạn và nhiều hơn nữa. Cypress là một trình chạy dựa trên trình duyệt hỗ trợ kiểm thử thành phần. Vui lòng đọc trang so sánh của Vitest để biết thông tin mới nhất so sánh giữa Vitest và Cypress.
Thư Viện Mounting
Việc kiểm thử thành phần thường liên quan đến việc gắn kết thành phần đang được kiểm thử độc lập, kích hoạt các sự kiện mô phỏng từ người dùng và xác nhận về đầu ra DOM được hiển thị. Có các thư viện tiện ích chuyên dụng giúp đơn giản hóa những công việc này.
@vue/test-utils
là thư viện kiểm thử thành phần cấp thấp chính thức được viết để cung cấp cho người dùng quyền truy cập vào các API cụ thể của Vue. Đây cũng là thư viện cấp thấp mà@testing-library/vue
được xây dựng dựa trên.@testing-library/vue
là một thư viện kiểm thử Vue tập trung vào việc kiểm thử các thành phần mà không phụ thuộc vào chi tiết triển khai. Nguyên tắc hướng dẫn của nó là đối với một bài kiểm thử càng giống với cách phần mềm được sử dụng, nó sẽ mang lại sự tự tin càng nhiều.
Chúng tôi khuyến nghị sử dụng @vue/test-utils
để kiểm thử các thành phần trong ứng dụng. @testing-library/vue
gặp vấn đề khi kiểm thử các thành phần bất đồng bộ với Suspense, nên nó nên được sử dụng cẩn thận.
Các Lựa Chọn Khác
Nightwatch là một trình chạy kiểm thử E2E với sự hỗ trợ kiểm thử thành phần Vue. (Dự Án Mẫu)
WebdriverIO cho việc kiểm thử thành phần chéo trình duyệt dựa trên tương tác người dùng tự nhiên dựa trên tự động hóa chuẩn. Nó cũng có thể được sử dụng với Testing Library.
Kiểm Thử Tích Hợp
Trong khi bài kiểm thử đơn vị mang lại sự tự tin cho nhà phát triển, bài kiểm thử đơn vị và thành phần giới hạn trong khả năng cung cấp bao phủ toàn diện cho một ứng dụng khi triển khai vào sản xuất. Do đó, bài kiểm thử từ đầu đến cuối (E2E) cung cấp bao phủ cho điều quan trọng nhất của một ứng dụng: điều gì sẽ xảy ra khi người dùng thực sự sử dụng ứng dụng của bạn.
Bài kiểm thử từ đầu đến cuối tập trung vào hành vi của ứng dụng đa trang thực hiện các yêu cầu mạng đối với ứng dụng Vue của bạn được xây dựng cho sản xuất. Thường thì chúng liên quan đến việc triển khai một cơ sở dữ liệu hoặc backend khác và thậm chí có thể chạy trên môi trường staging trực tiếp.
Bài kiểm thử từ đầu đến cuối thường sẽ bắt gặp vấn đề liên quan đến router, thư viện quản lý trạng thái, các thành phần cấp cao (ví dụ: một ứng dụng hoặc bố cục), tài sản công cộng, hoặc bất kỳ xử lý yêu cầu nào. Như đã nói ở trên, chúng bắt gặp những vấn đề quan trọng mà có thể là không thể bắt được bằng bài kiểm thử đơn vị hoặ
c bài kiểm thử thành phần.
Bài kiểm thử từ đầu đến cuối không nhập bất kỳ mã nguồn ứng dụng Vue nào của bạn mà thay vào đó hoàn toàn phụ thuộc vào việc kiểm thử ứng dụng của bạn bằng cách duyệt qua toàn bộ trang trong một trình duyệt thực sự.
Bài kiểm thử từ đầu đến cuối xác nhận nhiều lớp trong ứng dụng của bạn. Chúng có thể định mục tiêu ứng dụng được xây dựng cục bộ của bạn hoặc thậm chí môi trường staging thực. Kiểm thử trên môi trường Staging không chỉ bao gồm mã nguồn frontend của bạn và máy chủ tĩnh mà còn tất cả các dịch vụ backend và cơ sở hạ tầng liên quan.
Càng giống kiểm thử cách phần mềm được sử dụng, bài kiểm thử càng mang lại sự tự tin. - Kent C. Dodds - Tác giả của Testing Library
Bằng cách kiểm thử cách các hành động của người dùng ảnh hưởng đến ứng dụng của bạn, bài kiểm thử từ đầu đến cuối thường là chìa khóa để tăng cường sự tự tin về việc ứng dụng có hoạt động đúng cách hay không.
Chọn Lựa Giải Pháp Kiểm Thử Từ Đầu Đến Cuối
Trong khi việc kiểm thử từ đầu đến cuối (E2E) trên web đã có danh tiếng tiêu cực về các bài kiểm thử không đáng tin cậy (nổi) và làm chậm quá trình phát triển, các công cụ E2E hiện đại đã có những bước tiến để tạo ra các bài kiểm thử đáng tin cậy, tương tác và hữu ích hơn. Khi chọn một framework kiểm thử E2E, các phần sau cung cấp một số hướng dẫn về những điều cần lưu ý khi chọn framework kiểm thử cho ứng dụng của bạn.
Kiểm thử chéo trình duyệt
Một trong những lợi ích chính mà kiểm thử từ đầu đến cuối (E2E) được biết đến là khả năng kiểm thử ứng dụng của bạn trên nhiều trình duyệt. Mặc dù có vẻ hấp dẫn để có 100% phủ sóng kiểm thử chéo trình duyệt, quan trọng là lưu ý rằng kiểm thử chéo trình duyệt giảm giá trị trả về cho đội ngũ tài nguyên do thời gian và công suất máy tính bổ sung cần thiết để chạy chúng một cách đều đặn. Do đó, quan trọng là cân nhắc kỹ lưỡng về sự đánh đổi này khi chọn mức độ kiểm thử chéo trình duyệt cho ứng dụng của bạn.
Chuỗi phản hồi nhanh hơn
Một trong những vấn đề chính của việc kiểm thử từ đầu đến cuối (E2E) và phát triển là việc chạy toàn bộ bộ kiểm thử mất rất nhiều thời gian. Thông thường, điều này chỉ được thực hiện trong các đường ống liên tục tích hợp và triển khai (CI/CD). Các framework kiểm thử E2E hiện đại đã giúp giải quyết vấn đề này bằng cách thêm các tính năng như song song hóa, cho phép đường ống CI/CD thường chạy nhanh hơn nhiều so với trước. Ngoài ra, khi phát triển cục bộ, khả năng chạy một bài kiểm thử đơn lẻ cho trang bạn đang làm việc trên cùng với khả năng tải lại nhanh chóng của kiểm thử có thể giúp tăng cường quy trình làm việc và năng suất của nhà phát triển.
Trải nghiệm gỡ lỗi hàng đầu
Trong quá khứ, nhà phát triển thường phải dựa vào việc quét các nhật ký trong cửa sổ terminal để giúp xác định vấn đề xảy ra trong một bài kiểm thử. Tuy nhiên, các framework kiểm thử từ đầu đến cuối (E2E) hiện đại cho phép nhà phát triển tận dụng các công cụ mà họ đã quen thuộc, chẳng hạn như công cụ phát triển trình duyệt.
Tính Hiển Thị Ở Chế Độ Headless
Khi bài kiểm thử từ đầu đến cuối (E2E) chạy trong đường ống tích hợp/triển khai liên tục, chúng thường chạy trong trình duyệt không đồng bộ (headless browsers), tức là không có trình duyệt hiển thị cho người dùng xem. Một tính năng quan trọng của các framework kiểm thử E2E hiện đại là khả năng xem ảnh chụp và/hoặc video của ứng dụng trong quá trình kiểm thử, mang lại cái nhìn về lý do các lỗi xảy ra. Lịch sử cho thấy việc duy trì những kết nối này trước đây là công việc đầy mệt mỏi.
Đề Xuất
Tổng thể, chúng tôi tin rằng Cypress cung cấp giải pháp E2E hoàn chỉnh nhất với các tính năng như giao diện đồ họa thông tin, khả năng gỡ lỗi xuất sắc, các khẳng định và giả lập tích hợp, khả năng chống trược, song song hóa và chụp ảnh. Như đã nói ở trên, nó cũng hỗ trợ Kiểm Thử Thành Phần. Tuy nhiên, nó chỉ hỗ trợ trình duyệt dựa trên Chromium và Firefox.
Các Lựa Chọn Khác
Playwright cũng là một giải pháp kiểm thử E2E tuyệt vời với khả năng hỗ trợ nhiều trình duyệt hơn (chủ yếu là WebKit). Xem Tại Sao Playwright để biết thêm chi tiết.
Nightwatch là một giải pháp kiểm thử E2E dựa trên Selenium WebDriver. Điều này mang lại sự hỗ trợ trình duyệt rộng nhất.
WebdriverIO là một framework tự động hóa kiểm thử cho kiểm thử web và di động dựa trên giao thức WebDriver.
Các Mẹo
Thêm Vitest vào Dự Án
Trong dự án Vue dựa trên Vite, chạy:
sh
> npm install -D vitest happy-dom @testing-library/vue
Tiếp theo, cập nhật cấu hình Vite để thêm khối tùy chọn test
:
js
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
// ...
test: {
// bật các API kiểm thử toàn cục giống như Jest
globals: true,
// mô phỏng DOM với happy-dom
// (yêu cầu cài đặt happy-dom như một phụ thuộc đồng định)
environment: 'happy-dom'
}
})
TIP
Nếu bạn sử dụng TypeScript, thêm vitest/globals
vào trường types
trong tsconfig.json
của bạn.
json
// tsconfig.json
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
Sau đó, tạo một tệp kết thúc bằng *.test.js
trong dự án của bạn. Bạn có thể đặt tất cả các tệp kiểm thử trong một thư mục kiểm thử ở gốc dự án hoặc trong các thư mục kiểm thử kế bên các tệp nguồn của bạn. Vitest sẽ tự động tìm kiếm chúng bằng cách sử dụng quy ước đặt tên.
js
// MyComponent.test.js
import { render } from '@testing-library/vue'
import MyComponent from './MyComponent.vue'
test('nó nên hoạt động', () => {
const { getByText } = render(MyComponent, {
props: {
/* ... */
}
})
// khẳng định kết quả
getByText('...')
})
Cuối cùng, cập nhật package.json
để thêm kịch bản kiểm thử và chạy nó:
json
{
// ...
"scripts": {
"test": "vitest"
}
}
sh
> npm test
Kiểm Thử Composables
Phần này giả sử bạn đã đọc Composables section.
Khi đến việc kiểm thử composables, chúng ta có thể chia chúng thành hai loại: composables không phụ thuộc vào một ví dụ của thành phần chủ, và composables phụ thuộc vào nó.
Một composable phụ thuộc vào một ví dụ của thành phần chủ khi nó sử dụng các API sau:
- Lifecycle hooks
- Provide / Inject
Nếu một composable chỉ sử dụng các API Reactivity, thì nó có thể được kiểm thử bằng cách gọi trực tiếp và khẳng định trạng thái/phương thức trả về của nó:
js
// counter.js
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => count.value++
return {
count,
increment
}
}
js
// counter.test.js
import { useCounter } from './counter.js'
test('useCounter', () => {
const { count, increment } = useCounter()
expect(count.value).toBe(0)
increment()
expect(count.value).toBe(1)
})
Một composable phụ thuộc vào lifecycle hooks hoặc Provide / Inject cần được bao bọc trong một ví dụ của thành phần chủ để kiểm thử. Chúng ta có thể tạo một hàm trợ giúp như sau:
js
// test-utils.js
import { createApp } from 'vue'
export function withSetup(composable) {
let result
const app = createApp({
setup() {
result = composable()
// chặn cảnh báo mẫu thiếu
return () => {}
}
})
app.mount(document.createElement('div'))
// trả về kết quả và ví dụ ứng dụng
// để kiểm thử việc cung cấp / hủy cài đặt
return [result, app]
}
js
import { withSetup } from './test-utils'
import { useFoo } from './foo'
test('useFoo', () => {
const [result, app] = withSetup(() => useFoo(123))
// giả lập cung cấp để kiểm thử injections
app.provide(...)
// thực hiện khẳng định
expect(result.foo.value).toBe(1)
// kích hoạt hook onUnmounted nếu cần
app.unmount()
})
``
`
Đối với composables phức tạp hơn, cũng có thể dễ dàng kiểm thử chúng bằng cách viết các bài kiểm thử đối với thành phần bọc sử dụng các kỹ thuật [Kiểm Thử Thành Phần](#component-testing).