如何开始使用 Angular 单元测试

简介

如果您的项目是使用Angular CLI,]创建的,那么您就可以开始使用Jasmine作为测试框架和Karma作为测试运行者)编写测试了。

ANGLE还提供了TestBedasync等实用工具,以简化对异步代码、组件、指令或服务的测试。

在本文中,您将了解如何使用Jasmine和Karma编写和运行单元测试。

前提条件

要完成本教程,您需要:

本教程已经在Node v16.2.0、npmv7.15.1和@angular/corev12.0.4上进行了验证。

第一步-设置项目

您的测试文件通常放在它们测试的文件旁边,但是如果您愿意的话,它们也可以放在它们自己的单独目录中。

这些规范文件使用* .spec.ts命名约定。

首先,使用@angular/cli创建一个新项目:

1ng new angular-unit-test-example

然后,导航到新创建的项目目录:

1cd angular-unit-test-example

app.Component旁边会有一个app.Component.spec.ts文件。打开此文件并检查其内容:

 1[label src/app/app.component.spec.ts]
 2import { TestBed } from '@angular/core/testing';
 3import { AppComponent } from './app.component';
 4
 5describe('AppComponent', () => {
 6  beforeEach(async () => {
 7    await TestBed.configureTestingModule({
 8      declarations: [
 9        AppComponent
10      ],
11    }).compileComponents();
12  });
13
14  it('should create the app', () => {
15    const fixture = TestBed.createComponent(AppComponent);
16    const app = fixture.componentInstance;
17    expect(app).toBeTruthy();
18  });
19
20  it(`should have as title 'angular-unit-test-example'`, () => {
21    const fixture = TestBed.createComponent(AppComponent);
22    const app = fixture.componentInstance;
23    expect(app.title).toEqual('angular-unit-test-example');
24  });
25
26  it('should render title', () => {
27    const fixture = TestBed.createComponent(AppComponent);
28    fixture.detectChanges();
29    const compiled = fixture.nativeElement;
30    expect(compiled.querySelector('.content span').textContent).toContain('angular-unit-test-example app is running!');
31  });
32});

了解茉莉花

首先,关于茉莉,有几件事是必须知道的:

  • Describe块定义一个测试套件,每个it块用于单独的测试。
  • beForeEach在每次测试之前运行,用于测试的setup部分。
  • After Each在每次测试后运行,用于测试的`teardown‘部分。
  • 您也可以使用bepreAllAfter All,它们在所有测试之前或之后运行一次。
  • 您可以在Jasmine中使用expect测试断言,并使用toBeDefinedtoBeTruthytoContaintoEqualtoThrowtoBeNull、...例如:Expect(MyValue).toBeGreaterThan(3);
  • 可以用not进行否定断言:expect(MyValue).not.toBeGreaterThan(3);
  • 您也可以定义自定义匹配器。

TestBed是用于特定角度测试的主要工具。您将在测试套件的beforEach块中使用TestBed.figureTestingModule,并为其提供一个对象,该对象的值与常规声明提供者导入NgModule的值相似。然后,您可以链接一个对complementeComponents的调用,以告诉ANGLE编译声明的组件。

您可以使用TestBed.createComponent创建一个组件装置。灯具可以访问debugElement,这将使您能够访问组件灯具的内部。

变化检测不是自动完成的,所以您将在一个装置上调用DetectChanges来告诉ANGLE运行变化检测。

使用async包装测试的回调函数或bepreEach的第一个参数,可以让ANGLE执行异步编译,并等待async块内部的内容准备就绪后再继续。

理解测试

第一个测试名为应该创建App,它使用expect通过toBeTruthy()检查组件是否存在。

第二个测试被命名为应当有作为标题的角度-单元-测试-示例‘,它使用expect来检查app.tile值是否等于带有toEqual()的字符串`’角度-单元-测试-示例‘’。

第三个测试名为应当呈现标题,它使用expect通过toContain()检查编译后的代码中是否有文本‘角度-单元-测试-示例应用程序正在运行!’

在您的终端中,运行以下命令:

1ng test

将运行所有三个测试,并显示测试结果:

1[secondary_label Output]
23 specs, 0 failures, randomized with seed 84683
3AppComponent
4* should have as title 'angular-unit-test-example'
5* should create the app
6* should render title

这三项测试目前都在通过。

第二步-构建示例组件

让我们创建一个递增或递减某个值的组件。

在您的代码编辑器中打开app.Component.ts,并将以下代码行替换为incrementducment逻辑:

 1[label src/app/app.component.ts]
 2import { Component } from '@angular/core';
 3
 4@Component({
 5  selector: 'app-root',
 6  templateUrl: './app.component.html',
 7  styleUrls: ['./app.component.css']
 8})
 9export class AppComponent {
10  value = 0;
11  message!: string;
12
13  increment() {
14    if (this.value < 15) {
15      this.value += 1;
16      this.message = '';
17    } else {
18      this.message = 'Maximum reached!';
19    }
20  }
21
22  decrement() {
23    if (this.value > 0) {
24      this.value -= 1;
25      this.message = '';
26    } else {
27      this.message = 'Minimum reached!';
28    }
29  }
30}

在代码编辑器中打开app.Component.html,并将内容替换为以下代码:

 1[label src/app/app.component.html]
 2<h1>{{ value }}</h1>
 3
 4<hr>
 5
 6<button (click)="increment()" class="increment">Increment</button>
 7
 8<button (click)="decrement()" class="decrement">Decrement</button>
 9
10<p class="message">
11  {{ message }}
12</p>

至此,您应该已经有了app.Component.tsapp.Component.html的修订版本。

Step 3 -构建测试套件

使用您的代码编辑器重新访问app.Component.spec.ts,并将其替换为以下代码行:

 1[label src/app/app.component.spec.ts]
 2import { TestBed, async, ComponentFixture } from '@angular/core/testing';
 3import { By } from '@angular/platform-browser';
 4import { DebugElement } from '@angular/core';
 5
 6import { AppComponent } from './app.component';
 7
 8describe('AppComponent', () => {
 9  let fixture: ComponentFixture<AppComponent>;
10  let debugElement: DebugElement;
11
12  beforeEach(async(() => {
13    TestBed.configureTestingModule({
14      declarations: [
15        AppComponent
16      ],
17    }).compileComponents();
18
19    fixture = TestBed.createComponent(AppComponent);
20    debugElement = fixture.debugElement;
21  }));
22
23  it('should increment and decrement value', () => {
24    fixture.componentInstance.increment();
25    expect(fixture.componentInstance.value).toEqual(1);
26
27    fixture.componentInstance.decrement();
28    expect(fixture.componentInstance.value).toEqual(0);
29  });
30
31  it('should increment value in template', () => {
32    debugElement
33      .query(By.css('button.increment'))
34      .triggerEventHandler('click', null);
35
36    fixture.detectChanges();
37
38    const value = debugElement.query(By.css('h1')).nativeElement.innerText;
39
40    expect(value).toEqual('1');
41  });
42
43  it('should stop at 0 and show minimum message', () => {
44    debugElement
45      .query(By.css('button.decrement'))
46      .triggerEventHandler('click', null);
47
48    fixture.detectChanges();
49
50    const message = debugElement.query(By.css('p.message')).nativeElement.innerText;
51
52    expect(fixture.componentInstance.value).toEqual(0);
53    expect(message).toContain('Minimum');
54  });
55
56  it('should stop at 15 and show maximum message', () => {
57    fixture.componentInstance.value = 15;
58    debugElement
59      .query(By.css('button.increment'))
60      .triggerEventHandler('click', null);
61
62    fixture.detectChanges();
63
64    const message = debugElement.query(By.css('p.message')).nativeElement.innerText;
65
66    expect(fixture.componentInstance.value).toEqual(15);
67    expect(message).toContain('Maximum');
68  });
69});

我们在bepreEach块中直接赋值fix turedebugElement,因为我们的所有测试都需要它们。我们也会通过从@Angel/core/Testing导入ComponentFixture和从@Angel/core导入DebugElement来强类型它们。

在我们的第一个测试中,我们调用组件实例本身的方法。

在剩下的测试中,我们使用DebugElement触发按钮点击。注意DebugElement是如何有一个带有谓词的query方法的。这里我们使用By实用工具及其css方法来查找模板中的特定元素。DebugElement还有一个nativeElement方法,用于直接访问DOM。

在对Jasmine的expect进行断言之前,我们还在最后3个测试中使用了Fixture.DetectChanges来指示角度运行变化检测。

完成更改后,从终端运行ng test命令:

1ng test

这将在监视模式下启动Karma,因此您的测试将在每次文件更改时重新编译。

1[secondary_label Output]
24 specs, 0 failures, randomized with seed 27239
3AppComponent
4* should increment value in template
5* should increment and decrement value
6* should stop at 0 and show minimum message
7* should stop at 15 and show maximum message

所有四项测试都将通过。

结论

在本文中,您将了解如何使用Jasmine和Karma编写和运行单元测试。现在您已经了解了主要的角度测试实用程序,可以开始编写简单组件的测试了。

通过测试依赖组件、测试服务以及使用mocks,stubs和spies继续学习。

您也可以参考官方documentation]获取深入的角度测试指南。

Published At
Categories with 技术
Tagged with
comments powered by Disqus