- vừa được xem lúc

Cách sử dụng hỗ trợ Vitest thử nghiệm trong Angular 20 bên ngoài ng test

0 0 5

Người đăng: Vũ Tuấn

Theo Viblo Asia

Gần đây, nhóm Angular đã phát hành phiên bản 20 với nhiều cải tiến — hầu hết đã được thảo luận và giải thích rộng rãi ở nhiều nơi. Tuy nhiên, có một tính năng mới mà mình không tìm thấy nhiều thông tin: hỗ trợ thử nghiệm cho Vitest (ở dạng thử nghiệm).

Trước đó, đã có một số hỗ trợ Vitest dành cho Angular, nhờ dự án Analog và plugin của nó — mình cũng từng thử dùng từ thời Angular 18 và thấy hoạt động khá ổn. Nhưng giờ đây, Angular đã tự tích hợp hỗ trợ chính thức cho Vitest, điều này rất đáng chú ý. Vì vậy, mình quyết định tìm hiểu cách sử dụng.

May mắn thay, Angular đã cung cấp đủ tài liệu trong docs để giúp mình triển khai.

Tình huống thực tế

Mình đã thử nghiệm Angular 20 mới trong một dự án cá nhân để khám phá những tính năng này kết hợp với các công cụ mình yêu thích và thường dùng như VSCode, ESLint, Prettier,... Và mình đã thiết lập thành công.

Tuy nhiên, có một giới hạn quan trọng:

  • Hỗ trợ Vitest thử nghiệm chỉ hoạt động thông qua ng test.

Điều này không sao, nhưng với mình — một dev luôn thích cải thiện DX (developer experience) — thì như vậy chưa đủ. Mình muốn chạy test bằng npx vitest trực tiếp và dùng với Vitest extension chính thức của VSCode.

Giải pháp: dùng Vitest bên ngoài ng test

Sau khi thử đủ loại lỗi và nghiên cứu mã nguồn Angular, mình đã rút ra được một cách làm hoạt động được. Sau đây là kết quả, không cần bạn phải mày mò nhiều:

Yêu cầu ban đầu

  • Giả định bạn đã có một project Angular 20 đang chạy, đã cấu hình hỗ trợ Vitest, với component App và file test tương ứng.

1. src/test-setup.ts

import { NgModule } from '@angular/core';
import { ɵgetCleanupHook as getCleanupHook, getTestBed
} from '@angular/core/testing';
import { BrowserTestingModule, platformBrowserTesting
} from '@angular/platform-browser/testing';
import { afterEach, beforeEach } from 'vitest'; const providers: NgModule['providers'] = []; beforeEach(getCleanupHook(false));
afterEach(getCleanupHook(true)); @NgModule({ providers })
export class TestModule {} getTestBed().initTestEnvironment( [BrowserTestingModule, TestModule], platformBrowserTesting(), { errorOnUnknownElements: true, errorOnUnknownProperties: true }
);

2. vitest.config.ts

import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { root: './', globals: true, setupFiles: ['src/test-setup.ts'], environment: 'jsdom', watch: false, reporters: ['default'], coverage: { enabled: false, excludeAfterRemap: true } }, plugins: [ { name: 'angular-coverage-exclude', configureVitest(context) { context.project.config.coverage.exclude = ['**/*.{test,spec}.?(c|m)ts']; } } ]
});

3. src/app/app.spec.ts

import { readFileSync } from 'fs'; import { provideZonelessChangeDetection, ɵresolveComponentResources as resolveComponentResources
} from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { App } from './app'; describe('App', () => { beforeAll(async () => { await resolveComponentResources(url => Promise.resolve(readFileSync(new URL(url, import.meta.url), 'utf-8')) ); }); beforeEach(async () => { await TestBed.configureTestingModule({ imports: [App], providers: [provideZonelessChangeDetection()] }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(App); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); it('should render title', () => { const fixture = TestBed.createComponent(App); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ng20'); });
});

Những điều cần lưu ý

Mặc dù cách làm này hoạt động với Vitest ngoài ng test (với Angular 20.0.0), nó có một số rủi ro do tính thử nghiệm:

  • Phụ thuộc vào tính năng thử nghiệm, có thể sẽ thay đổi trong tương lai.
  • Sử dụng biến nội bộ của Angular, có thể không ổn định nếu framework thay đổi.
  • Chưa được thử nghiệm rộng rãi, có thể phát sinh lỗi ở các edge-case.

Kết luận

Cách làm này chủ yếu mang tính thử nghiệm, dành cho những ai yêu thích khám phá công cụ, và hy vọng cũng là một khởi đầu để mở ra thảo luận về các cách triển khai tốt hơn, ít rủi ro hơn.

Mọi góp ý mang tính xây dựng đều rất hoan nghênh.

Cảm ơn bạn đã đọc! 🎉

Bình luận

Bài viết tương tự

- vừa được xem lúc

Route in Angular JS

Router là một module được đặt tại @angular/router, cung cấp cho ứng dụng Angluar của chúng ta khả năng điều hướng và hiển thị nội dung phù hợp với địa chỉ URL. Với các ứng dụng web thông thường, việc điều hướng theo URL thường sẽ do phía server đảm nhiệm, giống như hình ảnh dưới đây là ví dụ với Rai

0 0 111

- vừa được xem lúc

Có gì mới trong Angular 11

Phiên bản Angular 11.0.0 được phát hành vào tháng 11 năm 2020. Bản phát hành chính Angular 11 cung cấp các bản cập nhật trên toàn bộ nền tảng, bao gồm CLI và các components.

0 0 117

- vừa được xem lúc

Pipe trong Angular

Xin chào tất cả mọi người, bài viết này minh xin giới thiệu về Pipe trong Angular, rất mong được mọi người theo dõi. 1) Pipe trong Angular là gì. . .

0 0 144

- vừa được xem lúc

Angular Reactive Forms: Cách sử dụng FormArray

Trong bài viết này chúng ta sẽ tìm hiểu khi nào và cách sử dụng FormArray, cả trong Component và trong Template, ngoài ra chúng ta sẽ xem cách để custom validation cho nó. Vì sao chúng ta cần sử dụng FormArray.

0 0 117

- vừa được xem lúc

RxJS và Reactive Programming

1 - Stream. Streams are a sequence of values over time.

0 0 371

- vừa được xem lúc

Mapping class Java với Angular Typescript model – Chưa bao giờ dễ đến thế

Xin chào mọi người hôm nay mình giới thiệu một loại đồ chơi cực xịn cực hay ho luôn, đây là một thư viện giúp cho mọi người tạo ra 1 class Typescript trong dự án Frontend ở đây mình lấy ví dụ là Angul

0 0 105