Angular13 Angular 测试工具API

2024-02-25 开发教程 Angular13 匿名 4

测试实用工具 API

本页面描述了一些最有用的 Angular 测试特性。

Angular 测试实用工具包括 ​TestBed​、​ComponentFixture ​以及一些控制测试环境的函数。​TestBed ​和 ​ComponentFixture ​类是单独介绍的。

下面是一些独立函数的摘要,以使用频率排序:

函数

详情

waitForAsync

在一个特殊的async 测试区域中运行测试(​it​)的函数体或准备函数(​beforeEach​)。

fakeAsync

在一个特殊的fakeAsync 测试区域中运行测试(​it​)的函数体,以便启用线性风格的控制流。

tick

通过在 fakeAsync 测试区域中刷新定时器和微任务(micro-task)队列来仿真时间的流逝以及异步活动的完成。

好奇和执着的读者可能会喜欢这篇长博客:
Tasks, microtasks, queues and schedules

接受一个可选参数,它可以把虚拟时钟往前推进特定的微秒数。清除调度到那个时间帧中的异步活动。

inject

从当前的 TestBed 注入器中把一个或多个服务注入到一个测试函数中。它不能用于注入组件自身提供的服务。

discardPeriodicTasks

当 ​fakeAsync ​测试程序以正在运行的计时器事件任务(排队中的 ​setTimeOut ​和 ​setInterval ​的回调)结束时,测试会失败,并显示一条明确的错误信息。
一般来讲,测试程序应该以无排队任务结束。当待执行计时器任务存在时,调用 ​discardPeriodicTasks ​来触发任务队列,防止该错误发生。

flushMicrotasks

当 ​fakeAsync ​测试程序以待执行微任务(比如未解析的承诺)结束时,测试会失败并显示明确的错误信息。

一般来说,测试应该等待微任务结束。当待执行微任务存在时,调用 ​flushMicrotasks ​来触发微任务队列,防止该错误发生。

ComponentFixtureAutoDetect

一个服务提供者令牌,用于开启自动变更检测。

getTestBed

获取当前 ​TestBed ​实例。通常用不上,因为 ​TestBed ​的静态类方法已经够用。​TestBed ​实例有一些很少需要用到的方法,它们没有对应的静态方法。

TestBed 类摘要

TestBed ​类是 Angular 测试工具的主要类之一。它的 API 很庞大,可能有点过于复杂,直到你一点一点的探索它们。

传给 ​configureTestingModule ​的模块定义是 ​@NgModule​ 元数据属性的子集。

type TestModuleMetadata = {
providers?: any[];
declarations?: any[];
imports?: any[];
schemas?: Array<SchemaMetadata | any[]>;
};

每一个重载方法接受一个 ​MetadataOverride<T>​,这里 ​T​ 是适合这个方法的元数据类型,也就是 ​@NgModule​、​@Component​、​@Directive​ 或者 ​@Pipe​ 的参数。

type MetadataOverride<T> = {
add?: Partial<T>;
remove?: Partial<T>;
set?: Partial<T>;
};

TestBed ​的 API 包含了一系列静态类方法,它们更新或者引用全局的 ​TestBed ​实例。

在内部,所有静态方法在 ​getTestBed()​ 函数返回的当前运行时间的 ​TestBed ​实例上都有对应的方法。

在 ​BeforeEach()​ 内调用 ​TestBed ​方法,以确保在运行每个单独测试时,都有崭新的开始。

这里列出了最重要的静态方法,以使用频率排序。

方法

详情

configureTestingModule

测试垫片(karma-test-shim, browser-test-shim)创建了初始测试环境和默认测试模块。默认测试模块是使用基本声明和一些 Angular 服务替代品,它们是所有测试程序都需要的。
调用 configureTestingModule来为一套特定的测试定义测试模块配置,添加和删除导入、(组件、指令和管道的)声明和服务提供者。

compileComponents

在配置好测试模块之后,异步编译它。如果测试模块中的任何一个组件具有 templateUrlstyleUrls,那么你必须调用这个方法,因为获取组件的模板或样式文件必须是异步的。
调用完 compileComponents之后,TestBed的配置就会在当前测试期间被冻结。

createComponent<T>

基于当前 TestBed的配置创建一个类型为 T 的组件实例。调用 createComponent之后,TestBed的配置就会在当前测试期间被冻结。

overrideModule

替换指定的 NgModule的元数据。回想一下,模块可以导入其它模块。overrideModule方法可以深入到当前测试模块深处,修改其中一个内部模块。

overrideComponent

替换指定组件类的元数据,该组件类可能嵌套在一个很深的内部模块中。

overrideDirective

替换指定指令类的元数据,该指令可能嵌套在一个很深的内部模块中。

overridePipe

替换指定管道类的元数据,该管道可能嵌套在一个很深的内部模块中。

inject

从当前 TestBed注入器获取一个服务。inject函数通常都能胜任这项工作,但是如果它没法提供该服务时就会抛出一个异常。
如果该服务是可选的呢?
TestBed.inject()方法可以接受可选的第二参数,当 Angular 找不到指定的服务提供者时,就会返回该对象(下面这个例子中是 null):

expect(TestBed.inject(NotProvided, null)).toBeNull();
调用了 TestBed.inject之后然后通过调用,TestBed的配置就会在当前测试期间被冻结。

initTestEnvironment

为整套测试的运行初始化测试环境。
测试垫片(karma-test-shim, browser-test-shim)会为你调用它,所以你很少需要自己调用它。
这个方法只能被调用一次。如果确实需要在测试程序运行期间改变这个默认设置,那么先调用 resetTestEnvironment
指定 Angular 编译器工厂,PlatformRef,和默认 Angular 测试模块。以 @angular/platform-<platform_name>/testing/<platform_name>的形式提供非浏览器平台的替代品。

resetTestEnvironment

重设初始测试环境,包括默认测试模块在内。

少数 ​TestBed ​实例方法没有对应的静态方法。它们很少被使用。

ComponentFixture 类

TestBed.createComponent<T>​ 会创建一个组件 ​T​ 的实例,并为该组件返回一个强类型的 ​ComponentFixture​。

ComponentFixture ​的属性和方法提供了对组件、它的 DOM 和它的 Angular 环境方面的访问。

ComponentFixture 的属性

下面是对测试最重要的属性,以使用频率排序。

属性

详情

componentInstance

TestBed.createComponent创建的组件类实例。

debugElement

与组件根元素关联的 DebugElement
debugElement提供了在测试和调试期间深入探查组件及其 DOM 元素的功能。它对于测试者是一个极其重要的属性。

nativeElement

组件的原生根 DOM 元素。

changeDetectorRef

组件的 ChangeDetectorRef
在测试一个拥有 ChangeDetectionStrategy.OnPush的组件,或者在组件的变化测试在你的程序控制下时,ChangeDetectorRef是最重要的。

ComponentFixture 方法

fixture 方法使 Angular 对组件树执行某些任务。在触发 Angular 行为来模拟的用户行为时,调用这些方法。

下面是对测试最有用的方法。

方法

详情

detectChanges

为组件触发一轮变化检查。
调用它来初始化组件(它调用 ngOnInit)。或者在你的测试代码改变了组件的数据绑定属性值后调用它。Angular 不能检测到你已经改变了 personComponent.name属性,也不会更新 name的绑定,直到你调用了 detectChanges
之后,运行 checkNoChanges,来确认没有循环更新,除非它被这样调用:detectChanges(false)

autoDetectChanges

如果你希望这个夹具自动检测变更,就把这个设置为 true
当自动检测打开时,测试 fixture 监听 zone 事件,并调用 detectChanges。当你的测试代码直接修改了组件属性值时,你还是要调用 fixture.detectChanges来触发数据绑定更新。
默认值是 false,喜欢对测试行为进行精细控制的测试者一般保持它为 false

checkNoChanges

运行一次变更检测来确认没有待处理的变化。如果有未处理的变化,它将抛出一个错误。

isStable

如果 fixture 当前是稳定的,则返回 true。如果有异步任务没有完成,则返回 false

whenStable

返回一个承诺,在 fixture 稳定时解析。
要想在完成了异步活动或异步变更检测之后再继续测试,可以对那个承诺对象进行挂钩。

destroy

触发组件的销毁。

DebugElement

DebugElement ​提供了对组件的 DOM 的访问。

fixture.debugElement​ 返回测试根组件的 ​DebugElement​,通过它你可以访问(查询)fixture 的整个元素和组件子树。

下面是 ​DebugElement ​最有用的成员,以使用频率排序。

成员

详情

nativeElement

与浏览器中 DOM 元素对应(WebWorkers 时,值为 null)。

query

调用 query(predicate: Predicate<DebugElement>)会在子树的任意深度中查找并返回能和谓词函数匹配的第一个 DebugElement

queryAll

调用 queryAll(predicate: Predicate<DebugElement>)会在子树的任意深度中查找能和谓词函数匹配的所有 DebugElement

injector

宿主依赖注入器。比如,根元素的组件实例注入器。

componentInstance

元素自己的组件实例(如果有)。

context

为元素提供父级上下文的对象。通常是控制该元素的祖级组件实例。
当一个元素被 *ngFor重复,它的上下文为 NgForOf,它的 $implicit属性值是该行的实例值。比如,*ngFor="let hero of heroes"里的 hero

children

DebugElement的直接子元素。可以通过继续深入 children来遍历这棵树。

DebugElement ​还有 ​childNodes​,即 ​DebugNode ​对象列表。​DebugElement ​从 ​DebugNode ​对象衍生,而且通常节点(node)比元素多。测试者通常忽略普通节点。

parent

DebugElement的父级。如果 DebugElement是根元素,parent为 null。

name

元素的标签名字,如果它是一个元素的话。

triggerEventHandler

如果在该元素的 listeners集合中有相应的监听器,就根据名字触发这个事件。第二个参数是该处理器函数所需的事件对象。
如果事件缺乏监听器,或者有其它问题,考虑调用 nativeElement.dispatchEvent(eventObject)

listeners

元素的 @Output属性以及/或者元素的事件属性所附带的回调函数。

providerTokens

组件注入器的查询令牌。包括组件自己的令牌和组件的 providers元数据中列出来的令牌。

source

source 是在源组件模板中查询这个元素的处所。

references

与模板本地变量(比如 #foo)关联的词典对象,关键字与本地变量名字配对。

DebugElement.query(predicate)​ 和 ​DebugElement.queryAll(predicate)​ 方法接受一个条件方法,它过滤源元素的子树,返回匹配的 ​DebugElement​。

这个条件方法是任何接受一个 ​DebugElement ​并返回真值的方法。下面的例子查询所有拥有名为 ​content ​的模块本地变量的所有 ​DebugElement​:

// Filter for DebugElements with a #content reference
const contentRefs = el.queryAll( de => de.references['content']);

Angular 的 ​By ​类为常用条件方法提供了三个静态方法:

静态方法

详情

By.all

返回所有元素

By.css(selector)

返回符合 CSS 选择器的元素

By.directive(directive)

返回 Angular 能匹配一个指令类实例的所有元素

// Can find DebugElement either by css selector or by directive
const h2 = fixture.debugElement.query(By.css('h2'));
const directive = fixture.debugElement.query(By.directive(HighlightDirective));