Khi phát triển một ứng dụng sử dụng bất kỳ một ngôn ngữ lập trình nào, việc đảm bảo code của chúng ta viết ra đúng như những gì chúng ta muốn là điều rất quan trọng dù đoạn code đó có sai business logic đi nữa. Để làm được điều này, chúng ta phải có unit test cho code của mình. Trong bài viết này, mình sẽ giới thiệu với các bạn về unit testing trong Angular để các bạn hiểu rõ hơn về cách chúng ta sẽ viết unit test trong Angular như thế nào!
Đầu tiên, mình sẽ tạo mới một Angular project để làm ví dụ:
Mặc định, khi các bạn tạo Angular project sử dụng Angular CLI, 2 công cụ dùng để viết và chạy unit test trong Angular là Jasmine và Karma sẽ được include, cấu hình và một ví dụ về viết unit test sử dụng 2 công cụ này cũng được include trong tập tin app.component.spec.ts ở thư mục src/app.
Nói nôm na thì Jasmine là một framework với nhiều function giúp chúng ta có thể dễ dàng viết unit test trong Angular còn Karma là một công cụ giúp chúng ta có thể chạy được unit test. Chúng ta cũng có thể xem report, code coverage sử dụng công cụ Karma này.
Công cụ Jasmine chỉ để cho chúng ta viết unit test thôi nên các bạn sẽ không cần cấu hình gì cả, với Karma thì khác. Chúng ta phải cấu hình để nó có thể chạy, output report, hiển thị code coverage cho chúng ta. Mặc định khi tạo Angular project, các bạn sẽ thấy tập tin cấu hình của Karma karma.conf.js ở thư mục home của project với nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma') ], client: { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, './coverage/angular-unit-testing'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, restartOnFileChange: true }); }; |
Tập tin cấu hình này của Karma sẽ định nghĩa framework mà chúng ta sẽ sử dụng để viết unit test sử dụng thuộc tính “frameworks”, các thư viện cần thiết để Karma có thể làm các công việc mà chúng ta muốn như report, code coverage,… được định nghĩa trong thuộc tính “plugins”. Mặc định thì để chạy unit test, Karma sẽ sử dụng Chrome browser với karma-chrome-launcher và Karma có một chế độ chạy gọi là autoWatch, có nghĩa nếu có bất kỳ thay đối nào trong code của chúng ta, Karma sẽ tự động detect và chạy lại test. Các bạn có thể thay đổi các thông tin cấu hình mặc định này nếu muốn.
Một điểm nữa mà các bạn cần để ý là ở đây chúng ta có tập tin test.ts trong thư mục src đóng vai trò là tập tin đầu tiên Angular sẽ chạy khi chúng ta chạy test với câu lệnh ‘ng test”. Nội dung của tập tin này như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting() ); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); |
Như các bạn thấy, một đối tượng tên là TestBed sẽ được Angular khởi tạo trong tập tin test.ts, đây là đối tượng sẽ giúp chúng ta tạo mới và cấu hình Component mà chúng ta cần test. Ngoài ra, cũng trong tập tin test.ts này, chúng ta cũng sẽ thấy một đoạn code sẽ giúp Angular detect được các tập tin chứa chữ spec, đây là những tập tin sẽ định nghĩa unit test trong Angular đó các bạn!
OK, trên đây là những thông tin cơ bản mà các bạn cần biết để bắt đầu viết unit test trong Angular. Bây giờ chúng ta sẽ xem xét nội dung của tập tin app.component.spec.ts trong thư mục src/app để hiểu cách viết unit test các bạn nhé!
Nội dung của tập tin này như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import { TestBed, async } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); })); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); }); it(`should have as title 'angular-unit-testing'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app.title).toEqual('angular-unit-testing'); }); it('should render title in a h1 tag', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-unit-testing!'); }); }); |
Ở đây, chúng ta sẽ sử dụng function describe() của Jasmine để bắt đầu viết unit test cho Component của mình. Function này để tạo mới một nhóm các test cases (có thể gọi là một nhóm các specs) hay còn có thể gọi là test suite, cho Component của các bạn. Tham số của function này gồm mô tả về test suite và định nghĩa của chúng như sau:
1 2 3 4 5 6 |
/** * Create a group of specs (often called a suite). * @param description Textual description of the group * @param specDefinitions Function for Jasmine to invoke that will define inner suites a specs */ declare function describe(description: string, specDefinitions: () => void): void; |
Chúng ta có nhiều spec trong test suite, nếu mỗi một spec chúng ta phải đi cấu hình cho Component thì rất tốn công. Các bạn có thể sử dụng function beforeEach() để làm việc này. Để mỗi lần chạy một spec, Angular sẽ gọi function beforeEach() trước để cấu hình cho Component, sau đó trong mỗi spec, chúng ta chỉ việc tạo mới Component và sử dụng các Jasmine function để hiện thực code cho unit test mà thôi.
Trong ví dụ của mình thì nội dung của function beforeEach() đơn giản vì ứng dụng của chúng ta đơn giản. Đối với các ứng dụng lớn, các bạn sẽ cần phải làm nhiều hơn thế, vì có thể Component của các bạn sử dụng rất nhiều các Component khác hoặc nhiều thứ khác nữa.
Trong mỗi spec, các bạn sẽ cần tạo mới Component và tuỳ theo mục đích cần test là gì thì chúng ta sẽ sử dụng các function của Jasmine để hiện thực nó. Chúng ta sẽ sử dụng function expect() của Jasmine và các function trong interface Matchers namespace jasmine để làm assertion:
Để đảm bảo chất lượng code, hãy cố gắng cover hết tất cả các cases trong code của Component bằng unit testing các bạn nhé!