Angular 单元测试

举报
前端小盆友 发表于 2020/07/27 10:20:18 2020/07/27
【摘要】 什么是单元测试?隔离程序的每个部件,在隔离环境中运行测试用例。在单元测试中,所写的测试需要事先提供既定的输入值与相应的逻辑单元,检测输出结果,确定它是否与我们的预期结果匹配。建立环境Angular 官方提供的测试工具是 Jasmine 和 Karma。Angular CLI 会下载并安装试用 Jasmine 测试框架 测试 Angular 应用时所需的一切。使用CLI创建的项目是可以立即用于...
  • 什么是单元测试?

  • 隔离程序的每个部件,在隔离环境中运行测试用例。

  • 在单元测试中,所写的测试需要事先提供既定的输入值与相应的逻辑单元,检测输出结果,确定它是否与我们的预期结果匹配。

建立环境

Angular 官方提供的测试工具是 Jasmine 和 Karma。

Angular CLI 会下载并安装试用 Jasmine 测试框架 测试 Angular 应用时所需的一切。

使用CLI创建的项目是可以立即用于测试的,运行CLI命令 ng test即可。ng test 命令在监视模式下构建应用,并启动 karma 测试运行器

如果要指定执行某个目录或者文件中的测试用例可以使用命令 ng test --include='**/unit/test.spec.ts'

angular9以下版本可以使用命令ng test --main ./src/app/unit/test.spec.ts

或者使用fdescribe或fit,让程序只运行某个测试用例或测试用例集。

使用 ng test --help 可以查看其他相关命令参数。

配置

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

  • karma.conf.js :文件是为了告知 Karma 需要启用哪些插件、加载哪些测试脚本、需要哪些测试浏览器环境、测试报告通知方式、日志等等。具体配置内容参见 karma.conf.jshttps://karma-runner.github.io/5.0/config/configuration-file.html>

  • test.ts:angular.json 中指定的测试入口文件,其中初始化了测试环境以及指定所有测试文件。

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

  • 运行命令 ng test --code-coverage可启动代码覆盖率报告,或者angular.json 中配置为true,配置后每次直接运行ng test就会启动代码覆盖率报告

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

Jasmine

Angular 单元测试是使用 Jasmine 框架来编写的。

基础知识

describe(string, function):是 Jasmine 的全局函数,可以理解为一个测试集(Test Suite),主要功能是用来划分单元测试的。describe 可以嵌套使用。

it(string, function):可以理解为测试用例。Specs 通过调用 it 的全局函数来定义。每个 Spec 包含一个或多个 expectations 期望值来测试需要测试代码。

Jasmine 中的每个 expectation 是一个断言,可以是 true 或者 false。当每个 Spec 中的所有 expectations 都是 true,则通过测试。有任何一个 expectation 是 false,则未通过测试。而方法的内容就是测试主体。

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

JavaScript 的作用域的规则适用,所以在 describe 定义的变量对 Suite 中的任何 it 代码块都是可见的。

describe("The 'toBe' matcher compares with ===", function() {
 it("and has a positive case ", function() {
   expect(true).toBe(true);
 });
 it("and can have a negative case", function() {
   expect(false).not.toBe(true);
 });
});

Setup 和 Teardown

Setup 方法:

  • beforeAll:每个 suite(即 describe)中所有 spec(即 it)运行之前运行,整个suite里只运行一次

  • beforeEach:每个 spec(即 it)运行之前运行

Teardown 方法:

  • afterAll:每个 suite(即 describe)中所有 spec(即 it)运行之后运行

  • afterEach:每个 spec(即 it)运行之后运行

跳过或专注

在实际项目中,需要由于发布的版本需要选择测试用例包,xdescribe和xit能很方便的将不包含在版本中的测试用例排除在外。不过xdescribe和xit略有不同:

  • xdescribe:该 describe下的所有 it 将被忽略,jasmine 将直接忽略这些it,因此不会被运行

  • xit:运行到该 it 时,挂起它不执行

测试项目时,如果只需要运行某一个测试集可以使用 fdescribe 和 fit。

举例:组件测试

测试环境中创建一个组件,使用Angular TestBed进行测试。

TestBed 是 Angular 测试工具集提供的用于构建一个 @NgModule 测试环境。使用 TestBed.configureTestingModule() 来构建测试模块,它接受一个元数据对象,其中具有 @NgModule 中的绝大多数属性。

// 引入相关模块
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { BackupListComponent } from './backup-list.component';
import { DebugElement } from '@angular/core';

describe('HorizontalGridComponent test', () => {
  let component: HorizontalGridComponent;
let fixture: ComponentFixture<HorizontalGridComponent>;

   // 配置 TestBed 环境
  beforeEach(async(() => {
     TestBed.configureTestingModule({
        imports: [
          HttpClientTestingModule
        ],
        declarations: [HorizontalGridComponent],
 }).compileComponents();
  }));

beforeEach(() => {
       // 创建一个HorizontalGridComponent 的实例
 fixture = TestBed.createComponent(HorizontalGridComponent);
       // 使用 fixture.componentInstance 来访问组件实例
 component = fixture.componentInstance;
       // TestBed.createComponent 不能触发变更检测,使用 detectChanges() 触发检查。
 fixture.detectChanges();
});
 
it('should create', () => {
 expect(fixture).toBeDefined();
      expect(component).toBeDefined();
});

it('should have <h3> with "Hello"', () => {
       // const El: HTMLElement = fixture.nativeElement;
       const De: DebugElement = fixture.debugElement;
       const El: HTMLElement = De.nativeElement; // nativeElement相当于debugElement的一个属性
       // js原生的querySelector api去获取h3标签
       const p = El.querySelector('h3');
       // 判断h3标签的内容
       expect(p.textContent).toEqual('Hello');
   });

   it('should find the <h3> with fixture.debugElement.query(By.css)', () => {
       const De: DebugElement = fixture.debugElement;
       const El = De.query(By.css('h3')); // nativeElement相当于debugElement的一个属性
       // js原生的querySelector api去获取h3标签
       const p: HTMLElement = El.nativeElement;
       // 判断h3标签的内容
       expect(p.textContent).toEqual('Hello');
   });

  afterEach(() => {
     TestBed.resetTestingModule();
  });
});
  • 配置 TestBed 环境,对于带有外部引用(样式、模板)的组件来说初始化时需要调用 compileComponents() 进行编译。由于 compileComponents 是异步的,所以要使用 async() 函数处理

  • 置好 TestBed 之后,调用它的 createComponent() 方法,它会创建一个 HorizontalGridComponent 的实例,把相应的元素添加到测试运行器的 DOM 中,然后返回一个 ComponentFixture 对象。ComponentFixture 用来与所创建的组件及其 DOM 元素进行交互。

  • 使用 fixture.componentInstance 来访问组件实例。

  • 获取组件元素 nativeElement 和 DebugElement。 前者原生 DOM 元素,属性取决于运行环境,如果是浏览器,就提供浏览器的一些api,后者是由 Angular 进行包装可以安全的横跨其支持的所有平台运行并提供诸如 query 或者 triggerEventHandler 事件dom操作等方法。

  • 使用 By 类支持跨平台应用。

  • TestBed.createComponent 不能触发变更检测,使用 detectChanges() 触发检查。

运行完成后,控制台输出:

chrome浏览器输出:


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。