2022-03-11
原文:和
依赖注入是 .这种灵活的方法使我们的可声明和基于类的服务更易于单独测试。
Tree- 依赖移除了一个间接层,即模块,但是我们如何测试它们的 tree- 呢?我们将测试依赖于特定平台 API 的注入令牌的值工厂。
某些组件具有特定于浏览器的功能。我们将一起测试一个横幅,通知用户我们将终止对 11 的支持。适当的测试套件可以让我们有足够的信心,我们甚至不必在 11 中测试横幅。
我们必须小心,不要对复杂的集成场景过于自信。我们应始终确保在尽可能接近生产的环境中执行 QA(质量保证)测试。这意味着在真正的 11 浏览器中运行应用程序。
测试实用程序允许我们伪造依赖项以进行测试。我们将探索使用 CLI 的测试框架在测试环境中配置和解决依赖项的不同选项。
通过示例,我们将探索组件、组件初始化、自定义和模拟事件。我们甚至为非常精简但定义明确的测试用例创建自定义测试工具。
看一个例子。
我们创建一个依赖注入令牌,该令牌评估为指示当前浏览器是否为 11 的标志。
// user-agent.token.ts
import { InjectionToken } from '@angular/core';
export const userAgentToken: InjectionToken =
new InjectionToken('User agent string', {
factory: (): string => navigator.userAgent,
providedIn: 'root',
});
// is-internet-explorer-11.token.ts
import { inject, InjectionToken } from '@angular/core';
import { userAgentToken } from './user-agent.token';
export const isInternetExplorer11Token: InjectionToken =
new InjectionToken('Internet Explorer 11 flag', {
factory: (): boolean =>
/Trident/7.0.+rv:11.0/.test(inject(userAgentToken)),
providedIn: 'root',
});
要单独测试 11 标志提供程序,我们可以用 false 值替换。
我们注意到用户代理字符串提供程序从特定于平台的 API 中提取相关信息。为了学习,假设我们需要来自同一个全局导航器对象的附加信息。根据我们使用的测试运行器,API 甚至可能在测试环境中不可用。
为了能够创建虚假导航器配置,我们为导航器 API 创建了依赖注入令牌。我们可以在开发和测试期间使用这些虚假配置来模拟用户上下文。
// user-agent.token.ts
import { inject, InjectionToken } from '@angular/core';
import { navigatorToken } from './navigator.token';
export const userAgentToken: InjectionToken =
new InjectionToken('User agent string', {
factory: (): string => inject(navigatorToken).userAgent,
providedIn: 'root',
});
// navigator.token.ts
import { InjectionToken } from '@angular/core';
export const navigatorToken: InjectionToken =
new InjectionToken('Navigator API', {
factory: (): Navigator => navigator,
providedIn: 'root',
});
对于我们的第一个测试,我们将为 API 令牌提供一个 false 值,该令牌在工厂提供程序中用作用户代理字符串令牌的依赖项。
为了测试目的替换令牌提供者,我们向测试模块添加了一个覆盖提供者,类似于模块自己的提供者覆盖导入模块的提供者。
// navigator-api.spec.ts
import { inject, TestBed } from '@angular/core/testing';
import { navigatorToken } from './navigator.token';
import { userAgentToken } from './user-agent.token';
describe('Navigator API', () => {
describe('User agent string', () => {
describe('Provider', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: navigatorToken,
useValue: {
userAgent: 'Fake browser',
},
},
],
});
});
it(
'extracts the user agent string from the Navigator API token',
inject([userAgentToken], (userAgent: string) => {
expect(userAgent).toBe('Fake browser');
}));
});
});
});
请注意,在我们测试用户代理令牌及其提供者时,我们将令牌依赖项替换为错误值。
使用
测试实用程序为我们提供了不止一种解决依赖关系的方法。在这个测试中,我们使用了@/core/包中的函数(*不是@/core中的那个)。
注入函数允许我们通过在我们作为参数传递的数组中列出它们的标签来解决多个依赖项。每个依赖注入令牌都被解析并作为参数提供给测试用例函数。
示例:%2Fapp%-%.spec.ts
使用时
当我们使用没有声明的测试模块时,即使在同一个测试用例中,我们通常也可以覆盖多次。我们将在本文后面研究一个示例。
值得注意的是,使用测试功能时并非如此。它在执行测试用例函数体之前解决依赖关系。
我们可以使用静态方法 .le 和 .替换钩子中的和令牌提供程序。但是当我们使用注入测试特性解决依赖关系时,我们无法在测试用例之间更改提供者或在测试用例期间替换它。
解决测试中没有的依赖项的更灵活的方法是使用静态方法 .get。我们只需从测试用例函数或测试生命周期钩子中的任何位置传递我们想要解析的依赖注入令牌。
让我们看一下本机浏览器 API 的另一个示例,我们使用依赖注入令牌进行抽象以用于开发和测试。
取决于:
// location.token.ts
import { DOCUMENT } from '@angular/common';
import { inject, InjectionToken } from '@angular/core';
export const locationToken: InjectionToken =
new InjectionToken('Location API', {
factory: (): Location => inject(DOCUMENT).location,
providedIn: 'root',
});
// location-api.spec.ts
import { DOCUMENT } from '@angular/common';
import { TestBed } from '@angular/core/testing';
import { locationToken } from './location.token';
describe('Location API', () => {
describe('Provider', () => {
it('extracts the location from the DOCUMENT token', () => {
TestBed.configureTestingModule({
providers: [
{
provide: DOCUMENT,
useValue: {
location: {
href: 'Fake URL',
},
},
},
],
});
const location: Location = TestBed.get(locationToken);
expect(location.href).toBe('Fake URL');
});
});
});
我们让依赖注入系统使用静态 .get 方法解析 API。如测试项目所示,文档令牌已成功伪造,并用于使用其真实的工厂提供程序解析被测令牌。
使用时
在之前的测试中,我们通过在测试模块中提供令牌的文档来用假对象替换文档。如果我们不这样做,则会提供全局文档对象。
此外,如果我们想测试不同的文档配置,如果我们不为文档令牌创建测试,我们将无法这样做。
在我们使用 .le 添加测试提供者的情况下,我们可以使用静态方法。在各种测试用例中用不同的假值替换它。在测试 11 个检测和 11 个横幅组件时,我们将使用此技术创建一个测试工具。
请注意,这只有在我们不使用 .一旦我们调用 .,测试平台的依赖就被锁定了。
价值与
在本文的第一部分中,我们介绍了一个在其提供者中具有值工厂的令牌。值工厂评估用户代理字符串是否代表 11 个浏览器。
为了测试值工厂中的浏览器检测,我们从真实浏览器中收集了一些用户代理字符串并将它们放入枚举中。
// fake-user-agent.ts
export enum FakeUserAgent {
Chrome = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
InternetExplorer10 = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)',
InternetExplorer11 = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko',
Firefox = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0',
}
在 11 个检测测试套件中,我们将几乎孤立地测试令牌。但真正的业务逻辑价值在于它的工厂提供者,它依赖于用户代理令牌。
用户代理令牌从 API 令牌中提取其值,但 API 测试套件已涵盖该依赖项。我们将通过选择用户代理令牌作为依赖链中的适当位置来开始伪造依赖关系。
// internet-explorer-11-detection.spec.ts
import { TestBed } from '@angular/core/testing';
import { isInternetExplorer11Token } from './is-internet-explorer-11.token';
import { FakeUserAgent } from './fake-user-agent';
import { userAgentToken } from './user-agent.token';
describe('Internet Explorer 11 detection', () => {
function setup({ userAgent }: { userAgent: string }) {
TestBed.overrideProvider(userAgentToken, { useValue: userAgent });
return {
isInternetExplorer11: TestBed.get(isInternetExplorer11Token),
};
}
const nonInternetExplorerUserAgents: ReadonlyArray =
Object.entries(FakeUserAgent)
.filter(([browser]) =>
!browser.toLowerCase().includes('internetexplorer'))
.map(([_browser, userAgent]) => userAgent);
it('accepts an Internet Explorer 11 user agent', () => {
const { isInternetExplorer11 } = setup({
userAgent: FakeUserAgent.InternetExplorer11,
});
expect(isInternetExplorer11).toBe(true);
});
it('rejects an Internet Explorer 10 user agent', () => {
const { isInternetExplorer11 } = setup({
userAgent: FakeUserAgent.InternetExplorer10,
});
expect(isInternetExplorer11).toBe(false);
});
it('rejects other user agents', () => {
nonInternetExplorerUserAgents.forEach(userAgent => {
const { isInternetExplorer11 } = setup({ userAgent });
expect(isInternetExplorer11).toBe(
false,
`Expected to reject user agent: "${userAgent}"`);
});
});
});
在指定测试用例之前,我们创建了一个测试设置函数,并从我们的假用户代理字符串中减少了一组非用户代理。
测试设置函数采用用户代理并使用它来伪造用户代理令牌提供程序。然后我们返回一个具有属性的对象,该对象具有通过 .get 方法从 Token 评估的值。
让我们先测试一下快乐的路径。我们传递了 11 个用户代理字符串,并期望被测令牌通过依赖注入系统评估为真。从测试项目中可以看出,浏览器检测按预期工作。
当用户浏览 10 时会发生什么?我们的测试套件显示 11 在这种情况下不会导致误报。
换句话说,当在依赖令牌中提供 10 个用户代理字符串时,被测令牌评估为 false。如果这不是预期用途,我们需要更改检测逻辑。现在我们已经对其进行了测试,很容易证明更改何时会成功。
最终测试在枚举定义的非浏览器上执行浏览器检测。测试用例遍历用户代理字符串,伪造用户代理提供者,评估令牌并期望它为假。如果不是这种情况,测试运行程序会显示有用的错误消息。
在测试中
现在我们对 11 个浏览器检测感到满意,创建和显示弃用横幅很简单。
// internet-explorer-11-banner.component.ts
import { Component, Inject } from '@angular/core';
import { isInternetExplorer11Token } from './is-internet-explorer-11.token';
@Component({
selector: 'internet-explorer-11-banner',
templateUrl: './internet-explorer-11-banner.component.html',
})
export class InternetExplorer11BannerComponent {
private isDismissed = false;
get isBannerVisible() {
return this.isInternetExplorer11 && !this.isDismissed;
}
constructor(
@Inject(isInternetExplorer11Token) private isInternetExplorer11: boolean,
) {}
onDismiss() {
this.isDismissed = true;
}
}
解除状态只是作为本地 UI 状态存储在私有组件属性中,供计算属性使用。
横幅组件有一个依赖项——Token,它被评估为一个布尔值。感谢装饰器,这个布尔值是通过横幅组件构造函数注入的。
在本文中,我们演示了如何在项目中测试和伪造树依赖。我们还测试了依赖于特定平台 API 的价值工厂。
在此过程中,我们调查了使用注入测试功能解决依赖关系的问题。使用 ,我们解决依赖注入令牌并探索这种方法的缺陷。
我们以多种方式测试了 11 个已弃用的横幅,因此几乎不需要在实际浏览器中对其进行测试。我们在其组件测试套件中伪造了它的依赖关系,但正如我们所讨论的,对于复杂的集成场景,我们应该始终在真实的浏览器目标中对其进行测试。
分类:
技术要点:
相关文章:
暂无评论内容