介绍
如果您的项目是使用 Angular CLI 创建的,那么一切准备就绪,您可以开始使用 Jasmine 作为测试框架并使用 Karma 作为测试运行器来编写测试.
Angular 还提供了诸如 TestBed 和 async 之类的实用程序,以简化异步代码、组件、指令或服务的测试。
在本文中,您将了解如何使用 Jasmine 和 Karma 在 Angular 中编写和运行单元测试。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
- 熟悉 设置 Angular 项目 。
本教程已使用 Node v16.2.0、npm v7.15.1 和 @angular/core v12.0.4 进行了验证。
第 1 步 — 设置项目
您的测试文件通常放在他们测试的文件旁边,但如果您愿意,它们也可以放在自己单独的目录中。
这些规范文件使用 *.spec.ts 的命名约定。
首先,使用 @angular/cli 创建一个新项目:
ng new angular-unit-test-example
然后,导航到新创建的项目目录:
cd angular-unit-test-example
除了 app.component 之外,还有一个 app.component.spec.ts 文件。 打开此文件并检查其内容:
src/app/app.component.spec.ts
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'angular-unit-test-example'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('angular-unit-test-example');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('angular-unit-test-example app is running!');
});
});
了解茉莉花
首先,关于茉莉花的一些重要知识:
describe块定义了一个测试套件,每个it块用于单独的测试。beforeEach在每次测试之前运行,用于测试的setup部分。afterEach在每次测试后运行,用于测试的teardown部分。- 您还可以使用
beforeAll和afterAll,它们在所有测试之前或之后运行一次。 - 您使用
expect并使用toBeDefined、toBeTruthy、toContain、toEqual、[ X121X]、toBeNull、... 例如:expect(myValue).toBeGreaterThan(3); - 您可以使用
not进行否定断言:expect(myValue).not.toBeGreaterThan(3); - 您还可以定义自定义匹配器。
TestBed 是可用于 Angular 特定测试的主要实用程序。 您将在测试套件的 beforeEach 块中使用 TestBed.configureTestingModule 并为其提供一个与 declarations、[ 的常规 NgModule 具有相似值的对象X142X] 和 imports。 然后你可以调用 compileComponents 来告诉 Angular 编译声明的组件。
您可以使用 TestBed.createComponent 创建一个 component fixture。 夹具可以访问 debugElement,这将使您能够访问组件夹具的内部。
更改检测不会自动完成,因此您将在夹具上调用 detectChanges 来告诉 Angular 运行更改检测。
用 async 包装测试的回调函数或 beforeEach 的第一个参数允许 Angular 执行异步编译并等待 async 块内的内容准备好之前继续。
了解测试
第一个测试名为 should create the app,它使用 expect 检查是否存在具有 toBeTruthy() 的组件。
第二个测试名为 should have as title 'angular-unit-test-example',它使用 expect 来检查 app.title 值是否等于字符串 'angular-unit-test-example' 和 toEqual()。
第三个测试名为 should render title,它使用 expect 来检查文本 'angular-unit-test-example app is running!' 和 toContain() 的编译代码。
在您的终端中,运行以下命令:
ng test
所有三个测试都将运行并显示测试结果:
Output3 specs, 0 failures, randomized with seed 84683 AppComponent * should have as title 'angular-unit-test-example' * should create the app * should render title
目前所有三个测试都通过了。
第 2 步 — 构建示例组件
让我们创建一个增加或减少值的组件。
在代码编辑器中打开 app.component.ts 并将以下代码行替换为 increment 和 decrement 逻辑:
src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
value = 0;
message!: string;
increment() {
if (this.value < 15) {
this.value += 1;
this.message = '';
} else {
this.message = 'Maximum reached!';
}
}
decrement() {
if (this.value > 0) {
this.value -= 1;
this.message = '';
} else {
this.message = 'Minimum reached!';
}
}
}
在代码编辑器中打开 app.component.html 并将内容替换为以下代码:
src/app/app.component.html
<h1>{{ value }}</h1>
<hr>
<button (click)="increment()" class="increment">Increment</button>
<button (click)="decrement()" class="decrement">Decrement</button>
<p class="message">
{{ message }}
</p>
此时,您应该已经有了 app.component.ts 和 app.component.html 的修订版本。
第 3 步 — 构建测试套件
使用您的代码编辑器重新访问 app.component.spec.ts 并将其替换为以下代码行:
src/app/app.component.spec.ts
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
let debugElement: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
fixture = TestBed.createComponent(AppComponent);
debugElement = fixture.debugElement;
}));
it('should increment and decrement value', () => {
fixture.componentInstance.increment();
expect(fixture.componentInstance.value).toEqual(1);
fixture.componentInstance.decrement();
expect(fixture.componentInstance.value).toEqual(0);
});
it('should increment value in template', () => {
debugElement
.query(By.css('button.increment'))
.triggerEventHandler('click', null);
fixture.detectChanges();
const value = debugElement.query(By.css('h1')).nativeElement.innerText;
expect(value).toEqual('1');
});
it('should stop at 0 and show minimum message', () => {
debugElement
.query(By.css('button.decrement'))
.triggerEventHandler('click', null);
fixture.detectChanges();
const message = debugElement.query(By.css('p.message')).nativeElement.innerText;
expect(fixture.componentInstance.value).toEqual(0);
expect(message).toContain('Minimum');
});
it('should stop at 15 and show maximum message', () => {
fixture.componentInstance.value = 15;
debugElement
.query(By.css('button.increment'))
.triggerEventHandler('click', null);
fixture.detectChanges();
const message = debugElement.query(By.css('p.message')).nativeElement.innerText;
expect(fixture.componentInstance.value).toEqual(15);
expect(message).toContain('Maximum');
});
});
我们直接在 beforeEach 块中分配 fixture 和 debugElement 因为我们所有的测试都需要这些。 我们还通过从 @angular/core/testing 导入 ComponentFixture 和从 @angular/core 导入 DebugElement 来强类型化它们。
在我们的第一个测试中,我们调用组件实例本身的方法。
在剩下的测试中,我们使用我们的 DebugElement 来触发按钮点击。 注意 DebugElement 有一个 query 方法接受一个谓词。 在这里,我们使用 By 实用程序及其 css 方法在模板中查找特定元素。 DebugElement 还有一个 nativeElement 方法,用于直接访问 DOM。
我们还在最后 3 个测试中使用 fixture.detectChanges 来指示 Angular 在使用 Jasmine 的 expect 进行断言之前运行更改检测。
完成更改后,从终端运行 ng test 命令:
ng test
这将以监视模式启动 Karma,因此您的测试将在每次文件更改时重新编译。
Output4 specs, 0 failures, randomized with seed 27239 AppComponent * should increment value in template * should increment and decrement value * should stop at 0 and show minimum message * should stop at 15 and show maximum message
所有四项测试都将通过。
结论
在本文中,您将了解如何使用 Jasmine 和 Karma 在 Angular 中编写和运行单元测试。 现在您已经了解了主要的 Angular 测试实用程序,并且可以开始为简单组件编写测试了。
继续学习测试具有依赖关系的组件、测试服务以及使用 模拟、存根 和间谍。
您也可以参考 官方文档 以获得深入的 Angular 测试指南。