Angular单元测试组件

这篇具有很好参考价值的文章主要介绍了Angular单元测试组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章目录

前言

一、 单元测试是什么?

二、配置jasmine & karma

三、技术点

1. 变更监测detectChanges

2. 模拟异步fakeAsync

3. Spy

四、单元测试基础结构

1. describe

2. beforeEach\afterEach

3. it

4. expect

4.1 断言方法

5. configureTestingModule

6. compileComponents

7. createComponent

8. ComponentFixture

8.1 创建固件

8.2 常用属性

8.3 常用方法

8.4 By.css()

五、测试运行

1. 终端操作

2. 最终结果

总结



前言

        在实际的开发中,讲究高效性和实用性,所以组件库开发调试完成后,需要为每个组件编写对应的单元测试代码,并在做了代码审查和测试审查的基础上,达到高的代码覆盖率(即代码的覆盖程度,是一种度量方式)⽬标,从而更好的加快项目进度、提升代码质量。

        本文主要是整理了写Angular单元测试的一些基础知识点以及如何为通用组件编写单元测试代码。

一、 单元测试是什么?

        隔离程序的每个部件,单独运行测试用例,即保证代码指令,避免代码出现bug的一种测试方式。单元测试中,所写的测试需要事先提供既定的输入值与相应的逻辑单元,检测输出的结果与预期结果是否匹配。

二、配置jasmine & karma

        Angular 官方提供了测试工具是 Jasmine 和 Karma,其中 jasmine 是 Angular 单元测试使用的测试框架​,karma 是测试过程的管理工具,主要记录测试的过程以及反馈输出。

        Angular CLI 会下载并安装试用 Jasmine 测试框架测试  Angular 应用时所需的一切。使用CLI创建的项目是可以立即用于测试的,运行CLI命令 ng test 即可。ng test 命令在监视模式下构建应用,并启动 Karma 测试运行器。

        CLI 会生成 Jasmine 和 Karma 的配置文件,主要的配置文件包括 karma.conf.js 和 test.ts。

"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
  • 确保根目录下有 karma.conf.js 配置文件,它是为了告知 Karma 需要启用哪些插件、加载哪些测试脚本、需要哪些测试浏览器环境、测试报告通知方式、日志等等。

  • 确保 src 下有 test.ts 文件,它是angular.json 中指定的测试入口文件,不仅初始化了测试环境,还指定了所有的测试文件。

  • 测试文件的扩展名必须是 .spec.ts,这样工具才能识别出它是一个测试(或“规约”)文件。

三、技术点

1. 变更监测detectChanges

        Angular单元测试中,经常需要调​用 fixture.detectChanges() 强制触发变化检测,如果不调用,模板中绑定的数据就不会更新。

        Angular 通过 NgZone 控制可能造成模板数据更新的点,然后在合适的时候触发变化检测;然而测试环境中没有 NgZone,所以便需要手动调用变化检测。

2. 模拟异步fakeAsync

        程序中有异步操作是很常见的,比如setTimeout、Promise.then等,如果需要测试异步执行完成之后的结果,就需要用到 tick,而 tick 就必须运行在 fakeAsync 中。

        tick() 是模拟异步时间片的定时器,可以设置一个等待时长的参数(单位:毫秒),通过在 fakeAsync 测试区域中刷新定时器和微任务队列来仿真时间的流逝以及异步活动的完成。

        flush() 也是模拟异步时间片的定时器,flush() 的结束条件不是一个时间值,而是微任务​队列为空,例如Promise.then 的场景(ngModel 赋值触发的异步),就可以使用flush()。

3. Spy

        Spy 能够追踪函数的调用历史信息(是否被调用、调用参数列表、被请求次数等),所以jasmine 用它来模拟和监听函数调用。

        Spy 仅存在于定义它的 describe 和 it 方法块中,并且每次在 spec 执行完之后被销毁。

四、单元测试基础结构

        为大家理解的更清楚,先看下面一个测试文件的部分代码,然后再逐一讲解。

describe("SearchConditionComponent", () => {
  let component: SearchConditionComponent;
  let fixture: ComponentFixture<SearchConditionComponent>;
  
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [SearchConditionComponent],
      providers: [
        {
          provide: ComponentFixtureAutoDetect,
          useValue: true,
        },
      ],
    }).compileComponents();
    fixture = TestBed.createComponent(SearchConditionComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it("should create", () => {
    expect(component).toBeTruthy();
  });

  it("校验默认初始值", () => {
    expect(component.keySetting.labelField).toBe("label");
    expect(component.keySetting.valueField).toBe("value");
  });
}

1. describe

       定义整体测试描述的方法,第一个参数表示描述、第二个参数是一个函数,在函数中我们进行测试。

        describe 是 Jasmine 的全局函数,可以理解为一个测试集,主要功能是用来划分单元测试。可以嵌套使用,并且describe 定义的变量对该测试集中任何 it 代码块都是可见的。

2. beforeEach\afterEach

        写单元测试公共逻辑的地方,公共逻辑相对于每个it都是独立的,不会在多个测试用例之间共享任何数据。

        它们分别在每个it​执行之前和之后执行,afterEach 可以用来清除每个测试用例执行过程中产生的一些副作用,比如测试中需要给全局的服务初始化一些数据,那么就需要在 afterEach 中把初始化的数据清空。

3. it

       定义单元测试方法,第一个参数表示测试功能点、第二个参数是函数,在里面我们定义断言方法。it 是单元测试的最小单元, 对应于一个测试用例,每个 spec 包含一个或多个 expectations 期望值来测试需要测试的代码。测试用例大概的逻辑:手动构造相关参数,然后在测试用例中调用该方法,写出理想的预期。

4. expect

        定义断言方法,参数就是断言表达式,提供了针对值、spy等相关场景的断言。Jasmine 中每个 expectation 是一个断言,可以是 true 或者 false。当每个 spec 中的所有 expectations 都是 true,则通过测试,有任何一个 expectation 是 false,则未通过测试。

        每个 matchers 实现一个布尔值,在实际值和期望值之间比较。它负责通知 Jasmine,此 expectation 是真或者假。然后 Jasmine 会认为相应的 spec 是通过还是失败。所有的 expect 都可以使用 not 表示否定的断言。

4.1 断言方法

  • toBe  类似于`===`  
  • toEqual  比较变量字面量的值  
  • toMatch  匹配值与正则表达式  
  • toBeDefined  检验变量是否定义
  • toBeNull  检验变量是否为`null`
  • toBeTruthy  检查变量值是否能转换成布尔型`真`值
  • toBeFalsy  检查变量值是否能转换成布尔型`假`值
  • toContain  检查在数组中是否包含某个元素
  • toBeLessThan  检查变量是否小于某个数字
  • toBeGreaterThan  检查变量是否大于某个数字或者变量
  • toBeCloseTo  比较两个数在保留几位小数位之后,是否相等,用于数字的精确比较
  • toThrow  检查一个函数是否会throw异常
  • toHaveBeenCalled  检查一个监听函数是否被调用过
  • toHaveBeenCalledWith  检查监听函数调用时的参数匹配信息

5. configureTestingModule

        创建初始测试环境,为测试配置模块 Module,需要把该测试组件用到的依赖 Module、服务以及测试组件本身的定义统统配置好,否则测试是无法正常运行的。

6. compileComponents

        在配置好测试模块之后,异步编译它。调用完 compileComponents 之后,TestBed 的配置就会在当前测试期间被冻结。

7. createComponent

        配置 TestBed 后,便可以调用它的 ​createComponent()​ 方法。​TestBed.createComponent()​ 会创建组件​的实例,把对应元素添加到测试运行器的 DOM 中,并返回一个 ​ComponentFixture ​对象。

8. ComponentFixture

        ​ComponentFixture ​是访问测试组件的入口,用于与所创建的组件及其对应的元素进行交互。可以通过测试夹具(fixture)访问组件实例,并用 Jasmine 的期望断言来确认它是否存在。

let fixture: ComponentFixture<SearchConditionComponent>;
fixture = TestBed.createComponent(SearchConditionComponent);
component = fixture.componentInstance;
fixture.detectChanges();

8.1 创建固件

        一般需要在 beforeEach​ 中创建测试组件的实例,因为每个测试用例都需要对 Angular 组件进行创建、销毁,Angular 对这些操作进行了封装,通过 TestBed 创建组件口返回的对象就是一个ComponentFixture​ 类型。那么在后续的测试代码中,便可以直接使用 fixture​ 对象对组件进行各种验证。

8.2 常用属性

(1)componentInstance:获取测试组件实例

(2)debugElement:返回测试根组件的DebugElement实例,通过该实例可以访问(查询)fixture 的整个元素和组件子树。

(3)nativeElement:可以直接​fixture.nativeElement​提供组件的元素,但这是一种便利方法,其最终实现还是fixture.debugElement.nativeElement​。

Q:为什么使用这种迂回的路径访问元素呢???

        nativeElement 的属性依赖运行时的环境,若在非浏览器平台上运行这些测试,这些平台上可能没有 DOM,或者其模拟的 DOM 不支持完整的 HTMLElement ​API,该怎么办呢?

        Angular依靠 DebugElement ​抽象来在其支持的所有平台上安全地工作,不会创建 HTML 元素树,而会创建一个 DebugElement​ 树来封装运行时平台上的原生元素,最后再通过 ​nativeElement属性解包 DebugElement,​返回特定于平台的元素对象。

8.3 常用方法

        detectChanges():强制执行一次测试的组件变化检测,一般会在组件创建完成后强制执行一次变化检测。

8.4 By.css()

        虽然测试都是在浏览器中运行的,但有些应用可能至少要在某些时候运行在不同的平台上。比如,作为优化策略的一部分,某组件可能会首先在服务器上渲染,以便在连接不良的设备上更快地启动应用。而服务器端渲染器可能不支持完整 HTML 元素 API,若不支持​ querySelector​,之前的测试就会失败。

​    DebugElement 提供了适用于其支持的所有平台的查询方法。这些查询方法接受一个谓词函数,当 ​DebugElement ​树中的一个节点与选择条件匹配时,该函数返回 ​true​。

借助从浏览器平台为运行时平台导入 ​By​ 类来创建一个谓词如下:

import { By } from "@angular/platform-browser";

下面的例子用​ DebugElement.queryall()​ 和浏览器的 ​By.css​() 实现测试。

fixture.detectChanges();
const elem: DebugElement = fixture.debugElement;
const iconDebug = elem.queryAll(By.css(".close-icon"));
const aDebug = elem.queryAll(By.css("a"));
iconDebug.forEach((item, index) => {
const a: HTMLElement = aDebug[index].nativeElement;
expect(a.textContent).toContain(pageOptions[index]);

五、测试运行

1. 终端操作

(1)如何运行测试代码:命令行输入ng test

注:在test.ts文件夹下配置如下,当执行ng test时,项目目录src下,所有以.spec.ts结尾的文件都会进行测试。

// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

(2)如何查看测试结果:命令行输入ng test --code-coverage

注:在Karma.conf.js中配置coverage文件夹路径如下,便可以直接在项目目录下查看测试结果文件夹。

coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],

        或者 angular.json 中配置为 true,配置后每次直接运行 ng test 就会启动代码覆盖率报告。

"test": {
  "options": {
    "codeCoverage": true
  }
}

2. 最终结果

(1)ng test运行项目后,在终端找到地址http://localhost:9876/

angular单元测试,angular单元测试,angular.js,前端,单元测试,javascript

(2)将地址复制到浏览器中,打开显示如下(16个用例,0个失败):

angular单元测试,angular单元测试,angular.js,前端,单元测试,javascript

(3)打开项目目录coverage文件夹,想要查看哪个组件的代码测试结果,便到组件对应文件夹下,打开index.html即可。例如:no-data.component单元测试结果如下:

angular单元测试,angular单元测试,angular.js,前端,单元测试,javascript

        可见代码覆盖率分为以下几种指标:语句覆盖率、分支覆盖率、函数覆盖率、行覆盖率。


总结

        100%代码覆盖率是一个很难实现的愿景,并且100%覆盖率仅仅意味着所有的代码路径都被执行了,并非已经覆盖了所有的边缘情况,所以应该着眼于测试功能是否全部覆盖,并做代码审查和测试审查。文章来源地址https://www.toymoban.com/news/detail-547436.html

到了这里,关于Angular单元测试组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端框架之争:Vue.js vs. React.js vs. Angular

    🎉欢迎来到Web前端专栏~前端框架之争:Vue.js vs. React.js vs. Angular ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:架构设计 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水平有限,如果

    2024年02月07日
    浏览(93)
  • 如何使用前端框架(React、Angular、Vue.js等)?该如何选择?

    聚沙成塔·每天进步一点点 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一个系统而

    2024年02月07日
    浏览(60)
  • 大型医院云HIS系统:采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发 融合B/S版电子病历系统

    一套医院云his系统源码 采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发。融合B/S版电子病历系统,支持电子病历四级,HIS与电子病历系统均拥有自主知识产权。 文末卡片获取联系! 基于云计算技术的B/S架构的医院管理系统(简称云HIS),采用前后

    2024年02月03日
    浏览(50)
  • 【Angular开发】Angular中的高级组件

    在这个博客中,我将解释Angular中的几个高级组件和机制,它们增强了灵活性、可重用性和性能。 通过熟悉这些高级组件和机制,您可以提高您的Angular开发技能,并在应用程序中利用灵活性、可重用性和性能优化的能力。让我们开始吧! NgContent NgContent,或Angular中的内容投影

    2024年02月04日
    浏览(55)
  • angular前端环境搭建、安装angular

    1.下载node.js安装包(要求node版本大于12.20) Node.js官方网站 : https://nodejs.org/en/ 进入官网后,当前页面下载的是最新版本,如需要下载历史版本,点击红框标注的其他下载,在进入的新的页面底部,选择红框标准的先前版本,然后下载相应的版本,在跳转的页面下载win64的版

    2024年02月03日
    浏览(58)
  • Angular-03:组件模板

    各种学习后的知识点整理归纳,非原创! 将组件类中的数据显示在组件模板中,组件类数据发生变化时会自动同步到组件模板中。(数据驱动DOM) 语法:{{}},插值表达式。 DOM对象属性 语法:[属性名] // 属性名加中括号[] HTML标签属性 [attr.属性名] // attr.属性名加中括号[] 自

    2024年02月07日
    浏览(36)
  • angular实现全局组件

    之前我们实现全局组件的第一种方式。我们是在定义了组件的时候通过在declares:[component],然后exports出该组件。最后在页面中每次导入该组件,而这次我们将采用另一种方式来实现 1 新建公用组件: 2 新建一个share.module.ts,在该module中引入我们所有的公共组件,本例中只有一个

    2024年02月12日
    浏览(44)
  • Angular组件通信

    给子组件标签自定义一个属性 子组件引入 Input 模块 父组件: 子组件: Angular的Output属性是用于子组件向父组件传递信息的一种方式。通过在子组件中定义一个Output属性,子组件可以通过EventEmitter触发这个属性,父组件可以通过@Output的形式监听子组件的属性,并在属性被触发

    2024年02月05日
    浏览(40)
  • Angular系列教程之组件

    在Angular中,组件是构建Web应用程序的核心单元。它们允许我们将UI划分为独立且可重用的部分,并通过数据绑定和事件处理等机制来实现交互性。本文将介绍Angular组件的基本概念,并说明组件和指令的关系。 组件是一个由HTML模板、样式和逻辑代码组成的独立单元。它可以看

    2024年01月17日
    浏览(38)
  • Angular组件生命周期详解

    当 Angular 实例化组件类 并渲染组件视图及其子视图时,组件实例的生命周期就开始了。生命周期一直伴随着变更检测,Angular 会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就结束了。当 Ang

    2024年02月05日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包