[转]Angular 单元测试讲解

 

 

  • Angular_单元测试
    • 测试分类
      • 按开发阶段划分
      • 按是否运行划分
      • 按是否查看源代码划分
      • 其他
    • ATDD,TDD,BDD,DDD
      • ATDD
      • TDD
      • BDD
      • DDD
    • Angular单元测试
      • Karma的介绍
      • jasmine介绍
      • 单元测试的好处
      • 使用jasmine和karma创建一个Angular项目
      • Karma配置
      • Test.ts文件
      • 测试体验
      • 测试Form
      • 测试服务service
      • 常用断言方法
      • Mock
        • Mock服务实例

 

 

 

Angular_单元测试

 

测试分类

 

image

 

按开发阶段划分

 

  • 单元测试
    单元测试又称模块测试,针对软件设计中的最小单位——程序模块,进行正确性检查的测试工作。
  • 集成测试
    集成测试又叫组装测试,通常在单元测试的基础上,将所有程序模块进行有序的、递增测试。重点测试不同模块的接口部分
  • 系统测试
    指的是将整个软件系统看成一个整体进行测试,包括对功能、性能以及软件所运行的软硬件环境进行测试。
  • 验收测试
    指按照项目任务书或合同、供需双方约定的验收依据文档进行的对整个系统的测试与评审,决定是否接收或拒收系统

 

按是否运行划分

 

  • 静态测试
    是指不实际运行被测软件,而只是静态地检查程序代码、界面或文档中可能存在的错误过程
  • 动态测试
    是指实际运行被测程序,输入相应的测试数据,检查实际输出结果和预期结果是否相符的过程。

 

按是否查看源代码划分

 

  • 黑盒测试
    指的是把被测的软件看做一个黑盒子,不关心盒子里面的结构是什么样子,只关心软件的输入数据和输出数据。
  • 白盒测试
    指的是把盒子打开,去研究里面的源代码和程序结构。

 

其他

 

  • 回归测试
    是指软件被修改后重新进行的测试,重复执行上一个版本测试时的用例,是为了保证对软件所做的修改没有引入新的错误而重复进行的测试。
  • 冒烟测试
    是指在对一个新版本进行系统大规模的测试之前,先验证一下软件的基本功能是否实现,是否具备可测性。
  • 随机测试
    是指测试中所有的输入数据都是随机生成的,其目的是模拟用户的真实操作,并发现一些边缘性的错误。

 

ATDD,TDD,BDD,DDD

 

ATDD

 

ATDD: Acceptance Test Driven Development(验收测试驱动开发)

 

TDD 只是开发人员的职责,通过单元测试用例来驱动功能代码的实现。在准备实施一个功能或特性之前,首先团队需要定义出期望的质量标准和验收细则,以明确而且达成共识的验收测试计划(包含一系列测试场景)来驱动开发人员的TDD实践和测试人员的测试脚本开发。面向开发人员,强调如何实现系统以及如何检验。

 

TDD

 

TDD: Test-driven development (测试驱动开发)

 

是一种使用自动化单元测试来推动软件设计并强制依赖关系解耦的技术。使用这种做法的结果是一套全面的单元测试,可随时运行,以提供软件可以正常工作的反馈。

 

测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。TDD首先考虑使用需求(对象、功能、过程、接口等),主要是编写测试用例框架对功能的过程和接口进行设计,而测试框架可以持续进行验证。。

 

BDD

 

BDD:Behavior-Driven Development (行为驱动开发)

 

行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。主要是从用户的需求出发,强调系统行为。BDD最初是由Dan North在2003年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。

 

image

 

DDD

 

DDD:领域驱动开发(Domain Drive Design)

 

DDD指的是Domain Drive Design,也就是领域驱动开发,DDD实际上也是建立在这个基础之上,因为它关注的是Service层的设计,着重于业务的实现,将分析和设计结合起来,不再使他们处于分裂的状态,这对于我们正确完整的实现客户的需求,以及建立一个具有业务伸缩性的模型。

 

Angular单元测试

 

Unit Test(单元测试)

 

image

 

对正式的项目进行单元测试是必须的,如果选择使用TDD(测试驱动开发)方法,则无关紧要,否则使用它将会产生很多好处。

 

在本文中,我们首先简单地提到单元测试的好处,然后我们将创建一个Angular单元测试的完整示例,使用jasminekarma

 

Karma的介绍

 

Karma是Testacular的新名字,在2012年google开源了Testacular,2013年Testacular改名为Karma。Karma是一个让人感到非常神秘的名字,表示佛教中的缘分,因果报应,比Cassandra这种名字更让人猜不透!

 

Karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner)。该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用。这个测试工具的一个强大特性就是,它可以监控(Watch)文件的变化,然后自行执行,通过console.log显示测试结果。

 

jasmine介绍

 

TDD(Test Driven Development)测试驱动开发,是敏捷开发中提出的最佳实践之一。jasmine很有意思的提出了BDD(Behavior Driven Development)行为驱动开发.

 

测试驱动开发,对软件质量起到了规范性的控制。未写实现,先写测试,一度成为Java领域研发的圣经。随着Javascript兴起,功能越来越多,代码量越来越大,开发人员素质相差悬殊,真的有必要建立对代码的规范性控制。jasmine就是为团队合作而生。

 

Jasmine是一个用来编写Javascript测试的框架,它不依赖于任何其它的javascript框架,也不需要对DOM。它有拥有灵巧而明确的语法可以让你轻松的编写测试代码。

 

jasmine的结构很简单:

 

describe("A suite", function() {var foo;beforeEach(function() {foo = 0;foo += 1;});afterEach(function() {foo = 0;});it("contains spec with an expectation", function() {expect(true).toBe(true);});
});

 

每个测试都在一个测试集中运行,Suite就是一个测试集,用describe函数封装。 Spec表示每个测试用例,用it函数封装。通过expect函数,作为程序断言来判断相等关系。setup过程用beforeEach函数封装,tearDown过程用afterEach封装。

 

单元测试的好处

 

我们先来看看我认为在解决方案中使用单元测试的主要原因…

 

  • 改进实现的设计
    开始编写一个功能而不给设计带来太多的思考是开发人员非常常见的错误。使用单元测试将强制思考并重新考虑设计,如果您使用TDD,则影响会更大。

  • 允许重构
    既然你已经有测试确保你所有的东西都能按预期工作,你可以很容易地添加对代码的修改,确保你没有添加任何错误。

  • 添加新功能而不会破坏任何内容
    当您添加新功能时,您可以运行测试以确保您不会破坏应用程序的任何其他部分。

 

还有更多,但这三个在任何项目上都是如此巨大的胜利,对于我来说,这些赢利是封闭式的。但如果你不相信,让我们再提几个。

 

  • 测试是很好的文档。
  • 测试使开发人员对他们的工作更有信心。

 

你可以说他们所有的好处都是以很高的成本来实现的,但是这完全是错误的。所有使用单元测试可能花费的时间与以后在您引入新功能或进行任何重构时要节省的时间相比将会很小。花在解决错误上的时间要比没有使用单元测试时大大缩短。

 

我们将创建一个使用AngularJasmineKarma的应用程序的小而完整的例子。

 

这些是我们要谈论的一些事情:

 

  • 解释一下工具Karma和Jasmine
  • 解释karma配置
  • 解释test文件
  • 创建第一个简单的测试,介绍Jasmine和Angular测试功能
  • 测试一个Angular form,介绍Jasmine和Angular测试功能
  • 测试一个带服务的组件,介绍Angular测试功能

 

使用jasmine和karma创建一个Angular项目

 

正如Angular团队建议我们要用Angular cli来创建我们的应用程序。通过这样做,jasmine和karma的配置可以帮我们解决,比较方便。

 

安装angular-cli并创建一个新项目:

 

  • npm install -g @angular/cli
  • ng new UnitTest –routing

 

当你创建项目时,所有的依赖关系都会安装,包括你需要创建测试的所有东西。

 

  "@types/jasmine": "~2.8.6","@types/jasminewd2": "~2.0.3","@types/node": "~8.9.4","codelyzer": "~4.2.1","jasmine-core": "~2.99.1","jasmine-spec-reporter": "~4.2.1","karma": "~1.7.1","karma-chrome-launcher": "~2.2.0","karma-coverage-istanbul-reporter": "~1.4.2","karma-jasmine": "~1.1.1","karma-jasmine-html-reporter": "^0.2.2",

 

  • jasmine-core:Jasmine是我们将用来创建测试的框架。它有许多功能可以让我们编写不同类型的测试。
  • karma:Karma是我们测试的任务跑步者。它使用配置文件来设置启动文件,报告,测试框架,浏览器等等。
  • 其余依赖主要为记录我们的测试,工具使用karma和jasmine和browser的发射器。

 

要运行测试,只需运行命令“ng test”。该命令将执行测试,打开浏览器,显示控制台和浏览器报告,同样重要的是,将测试执行保留为监视模式。也就是当我们修改过后,可以自动更新测试结果。

 

ng test

 

image

 

提示:如果想要终止,需要在终端内按CTRL+C

 

Karma配置

 

让我们来看看由angular-cli创建的karma配置文件。

 

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.htmlmodule.exports = function (config) {config.set({basePath: '',frameworks: ['jasmine', '@angular-devkit/build-angular'],plugins: [require('karma-jasmine'),require('karma-chrome-launcher'),require('karma-jasmine-html-reporter'),require('karma-coverage-istanbul-reporter'),require('@angular-devkit/build-angular/plugins/karma')],client: {clearContext: false // leave Jasmine Spec Runner output visible in browser},coverageIstanbulReporter: {dir: require('path').join(__dirname, '../coverage'),reports: ['html', 'lcovonly'],fixWebpackSourcePaths: true},reporters: ['progress', 'kjhtml'],port: 9876,colors: true,logLevel: config.LOG_INFO,autoWatch: true,browsers: ['Chrome'],singleRun: false});
};

 

你大概可以猜到这些配置属性的大部分用途,但我们来看看其中的一些。

 

  • frameworks:这是jasmine被设定为测试框架的地方。如果你想使用另一个框架,这是做这件事的地方。
  • reporters:负责将测试结果告知给开发者。通常是将结果打印到控制台上,或者存入文件中
  • autoWatch:如果设置为true,则测试将以Watch模式运行。如果您更改任何测试并保存文件,测试将重新生成并重新运行。
  • browsers:这是您设置测试应该运行的浏览器的位置。默认情况下是chrome,但你可以安装和使用其他浏览器启动器。

 

Test.ts文件

 

karma的angular-cli配置使用文件“test.ts”作为应用程序测试的入口点。我们来看看这个文件;

 

// This file is required by karma.conf.js and loads recursively all the .spec and framework filesimport 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {BrowserDynamicTestingModule,platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';declare const require: any;// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule,platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

 

你可能永远不需要改变这个文件,但是有时候还是会更改的,比如:某一个spec文件排除测试等。

 

测试体验

 

我们来创建我们的第一个测试。修改app.component.ts。这个组件只有一个属性“text”,其值为“Angular Unit Testing”,它是在HTML中的“h1”标记中呈现的,它还包含路由根元素和一些路由链接。让我们创建一个测试文件来检查组件是否实际具有该属性,并且实际上是在HTML中呈现的。

 

app.component.html文件

 

<h1>{{text}}</h1>
<router-outlet></router-outlet>

 

app.component.ts文件

 

import { Component } from '@angular/core';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent {text = 'Angular Unit Testing';
}

 

app.component.spec.ts文件

 

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {beforeEach(async(() => {TestBed.configureTestingModule({imports: [RouterTestingModule],declarations: [AppComponent],}).compileComponents();}));it('should create the app', async(() => {const fixture = TestBed.createComponent(AppComponent);const app = fixture.debugElement.componentInstance;expect(app).toBeTruthy();}));it(`should have as title 'app'`, async(() => {const fixture = TestBed.createComponent(AppComponent);const app = fixture.debugElement.componentInstance;expect(app.text).toEqual('Angular Unit Testing');}));it('should render title in a h1 tag', async(() => {const fixture = TestBed.createComponent(AppComponent);fixture.detectChanges();const compiled = fixture.debugElement.nativeElement;expect(compiled.querySelector('h1').textContent).toContain('Welcome to Angular Unit Testing!');}));
});

 

此时执行:

 

ng test

 

 

npm test

 

ng test的常用参数.
- –code-coverage -cc 代码覆盖率报告, 默认这个是不开启的, 因为生成报告的速度还是比较慢的.
- –colors 输出结果使用各种颜色 默认开启
- –single-run -sr 执行测试, 但是不检测文件变化 默认不开启
- –progress 把测试的过程输出到控制台 默认开启
- –sourcemaps -sm 生成sourcemaps 默认开启
- –watch -w 运行测试一次, 并且检测变化 默认开启

 

在弹出的chrome浏览器窗口中显示:

 

image

 

我们详细介绍一下这个测试代码

 

  1. 导入测试文件的所有依赖项
    这里要注意,你在组件内使用的依赖,这里面同样需要导入,否则会无法运行。

  2. 使用describe开始我们的测试

 

describe是一个函数,Jasmine 就是使用 describe 全局函数来测试的。

 

declare function describe(description: string, specDefinitions: () => void): void;

 

表示分组类似测试套,也就是一组测试用例,支持description嵌套。

 

例子:

 

describe('测试显示/隐藏筛选条件', ()=>{  })

 

  1. 我们在每个之前使用异步。异步的目的是让所有可能的异步代码在继续之前完成
  2. Jasmine 就是使用 it 全局函数来表示,和 describe 类似,字符串和方法两个参数。
    每个 Spec 内包括多个 expectation 来测试需要测试的代码,只要任何一个 expectation 结果为 false 就表示该测试用例为失败状态。

 

describe('demo test', () => {const VALUE = true;it('should be true', () => {expect(VALUE).toBe(VALUE);})
});

 

如果有很多需要测试的,可以多个it:

 

describe('AppComponent', () => {beforeEach(async(() => {TestBed.configureTestingModule({imports: [RouterTestingModule],declarations: [AppComponent],}).compileComponents();}));it('should create the app', async(() => {const fixture = TestBed.createComponent(AppComponent);const app = fixture.debugElement.componentInstance;expect(app).toBeTruthy();}));
});

 

断言,使用 expect 全局函数来表示,只接收一个代表要测试的实际值,并且需要与 Matcher 代表期望值

 

  1. TestBed可以帮助我们创建app实例
  2. 代码中有3个it

    • 第一个为异步测试app是否true或false
      如果app是0;两次取反当然是false;
      如果app是null;两次取反是false;
      如果app是undefined;两次取法是false;
      其余的,两次取反是true;

    • 第二个为异步测试app是否有text属性,并且判断值是否和预期相同

    • 第三个为异步测试app是否在h1标签中的显示值为预期值

 

测试Form

 

  1. 创建一个contact组件。

 

ng g c contact

 

  1. 首先我们修改contact.component HTML文件

 

<div>{{text}}</div><form id="contact-form" [formGroup]="contactForm" (ngSubmit)="onSubmit()" novalidate><div class="form-group"><label class="center-block">Name:<input class="form-control" formControlName="name"></label><label class="center-block">Email:<input class="form-control" formControlName="email"></label><label class="center-block">Text:<input class="form-control" formControlName="text"></label></div><button type="submit"[disabled]="!contactForm.valid" class="btn btn-success">Save</button></form>

 

这很简单,针对代码不做任何解释了。

 

  1. 修改contact.component.ts文件

 

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';@Component({selector: 'app-contact',templateUrl: './contact.component.html',styleUrls: ['./contact.component.css']
})
export class ContactComponent {text = 'contact page';contactForm: FormGroup;contact = {name: '',email: '',text: ''};submitted = false;constructor() {this.createForm();}createForm(): void {this.contactForm = new FormGroup({'name': new FormControl(this.contact.name, [Validators.required,Validators.minLength(4)]),'email': new FormControl(this.contact.email, [Validators.required,Validators.email]),'text': new FormControl(this.contact.text, Validators.required)});}onSubmit(): void {this.submitted = true;}
}

 

这个组件也很容易理解。 onSubmit提交函数只是将提交的属性更改为true。

 

  1. 修改app-routing.module.ts

 

import { ContactComponent } from './contact/contact.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';const routes: Routes = [{path: '',redirectTo: 'contact',pathMatch: 'full'},{path: 'contact',component: ContactComponent}
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule]
})
export class AppRoutingModule { }

 

此时终端执行:

 

npm start

 

image

 

  1. 修改测试文件contact.component.spec.ts

 

import { BrowserModule, By } from '@angular/platform-browser';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';import { ContactComponent } from './contact.component';
import { DebugElement } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';describe('ContactComponent', () => {let comp: ContactComponent;let fixture: ComponentFixture<ContactComponent>;let de: DebugElement;let el: HTMLElement;beforeEach(async(() => {TestBed.configureTestingModule({declarations: [ContactComponent],imports: [BrowserModule,FormsModule,ReactiveFormsModule]}).compileComponents().then(() => {fixture = TestBed.createComponent(ContactComponent);comp = fixture.componentInstance;de = fixture.debugElement.query(By.css('form'));el = de.nativeElement;});}));it(`should have as text 'contact page'`, async(() => {expect(comp.text).toEqual('contact page');}));it('should set submitted to true', async(() => {comp.onSubmit(); // 直接内部调用onSubmit函数, submitted被更改为trueexpect(comp.submitted).toBeTruthy();}));it('form call the onSubmit method', async(() => {fixture.detectChanges();spyOn(comp, 'onSubmit');el = fixture.debugElement.query(By.css('button')).nativeElement;el.click(); // 模拟在html界面上点击onSubmit,此时是不能被点击的,因为没有输入,所以次数应该是0expect(comp.onSubmit).toHaveBeenCalledTimes(0);}));it('form should be invalid', async(() => {comp.contactForm.controls['email'].setValue('');comp.contactForm.controls['name'].setValue('');comp.contactForm.controls['text'].setValue('');expect(comp.contactForm.valid).toBeFalsy();}));it('form should be vaild', async(() => {comp.contactForm.controls['email'].setValue('asd@asd.com');comp.contactForm.controls['name'].setValue('aada');comp.contactForm.controls['text'].setValue('text');expect(comp.contactForm.valid).toBeTruthy();}));
});

 

此时执行:

 

ng test

 

这里写图片描述

 

我们来分析一下,这个测试文件做了哪些东西?

 

  • 导入依赖模块BrowserModule,FormsModule,ReactiveFormsModule
  • 使用”By”将DOM中的form导入进来
  • 第一个测试text属性
  • 测试onSubmit函数调用
  • 第三个测试使用“fixture”对象的函数“detectChanges”将组件状态应用于HTML,然后从DOM获取提交按钮并触发单击事件。在此之前,我们在组件的“onSubmit”功能上创建一个jasmine “spy”。最后,我们期望onSubmit函数不会被执行,因为这个按钮应该被禁用,因为表单无效。
  • 第四个测试将无效值设置为组件表单,并期望表单有效属性为false。
  • 最后,在第五个测试中,我们将有效值设置为表单并期望表单有效属性为真。

 

小提示
- detectChanges
在测试中的Angular变化检测。每个测试程序都通过调用fixture.detectChanges()来通知Angular执行变化检测。
- By
By类是Angular测试工具之一,它生成有用的predicate。 它的By.css静态方法产生标准CSS选择器 predicate,与JQuery选择器相同的方式过滤。

 

测试服务service

 

当你要测试一个带有服务的组件时,就像我们已经看到的那样,你需要将提供者添加到在“beforeEach”中创建的测试模块。事情是,你可能不想使用实际的服务,而是一个模拟版本,所以让我们看看如何做到这一点……

 

  1. 创建一个app.service服务

 

ng g s app

 

  1. 修改app.service.ts

 

import { Injectable } from '@angular/core';@Injectable({providedIn: 'root'
})
export class AppService {constructor() { }getInfo(): string {return 'test service';}
}

 

  1. 修改app.component.ts

 

import { AppService } from './app.service';
import { Component } from '@angular/core';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent {text = 'Angular Unit Testing';info: string;constructor(private service: AppService) {this.info = this.service.getInfo();}
}

 

  1. 在app.component.spec.ts内增加
    注意引入AppService服务。

 

...providers: [AppService]
...it('should have as info test service', async(() => {const fixture = TestBed.createComponent(AppComponent);const app = fixture.debugElement.componentInstance;expect(app.info).toEqual('test service');}));

 

  1. 修改app.service.spec.ts

 

import { TestBed, inject, async } from '@angular/core/testing';import { AppService } from './app.service';describe('AppService', () => {beforeEach(() => {TestBed.configureTestingModule({providers: [AppService]});});it('should be created', inject([AppService], (service: AppService) => {expect(service).toBeTruthy();}));it('should getInfo test service', inject([AppService], (service: AppService) => {expect(service.getInfo()).toEqual('test service');}));
});

 

此时执行:

 

ng test

 

测试全部通过。

 

小提示
有些时候我们希望不是异步的,这时需要使用takeAsync函数,fakeAsync最重要的好处是测试程序看起来像同步的。

 

it('should show quote after getQuote promise (fakeAsync)', fakeAsync(() => {fixture.detectChanges();tick();                  // wait for async getQuotefixture.detectChanges(); // update view with quoteexpect(el.textContent).toBe(testQuote);
}));

 

常用断言方法

 

Jasmine 提供非常丰富的API,一些常用的Matchers:

 

  • toBe() 等同 ===
  • toNotBe() 等同 !==
  • toBeDefined() 等同 !== undefined
  • toBeUndefined() 等同 === undefined
  • toBeNull() 等同 === null
  • toBeTruthy() 等同 !!obj
  • toBeFalsy() 等同 !obj
  • toBeLessThan() 等同 <
  • toBeGreaterThan() 等同 >
  • toEqual() 相当于 ==
  • toNotEqual() 相当于 !=
  • toContain() 相当于 indexOf
  • toBeCloseTo() 数值比较时定义精度,先四舍五入后再比较。
  • toHaveBeenCalled() 检查function是否被调用过
  • toHaveBeenCalledWith() 检查传入参数是否被作为参数调用过
  • toMatch() 等同 new RegExp().test()
  • toNotMatch() 等同 !new RegExp().test()
  • toThrow() 检查function是否会抛出一个错误

 

而这些API之前用 not 来表示负值的判断。

 

expect(true).not.toBe(false);

 

这些Matchers几乎可以满足我们日常需求,当然你也可以定制自己的Matcher来实现特殊需求。

 

Mock

 

在实际的组件测试中发现组件往往依赖于服务。而服务又依赖于外部资源如http交互、本地资源等。为了屏蔽外部依赖方便组件的测试,可以对服务进行mock。对于服务的mock方式有两种:伪造服务实例(提供服务复制品)、刺探真实服务。这两种方式都能够达到mock的效果,我们可以挑选一种最适合自己当前测试文件的测试方式来进行测试。

 

Mock服务实例

 

第一步:编写服务的mock类

 

class TaskMonitorStubService extends TaskMonitorService {public queryTaskList(request: ViewTaskRequest): Observable<any> {return request.code === -1 ? Observable.of(runningTaskResponse): Observable.of(finishedTashResponse)}
}

 

第二步:在configureTestingModule用Mock的服务替换真实的服务

 

TestBed.configureTestingModule({imports: [HttpModule,TaskMonitorModule],Providers: [{provide: TaskMonitorService, useClass: TaskMonitorStubService}]
})

 

刺探真实服务

 

Angular的服务都是通过注入器注入到系统中的,同样我们可以从根TestBed获取到注入服务的实例,然后结合刺探(Spy)对真实的服务的方法进行替换.

 

let taskMonitorService: TaskMonitorService = TestBe.get(TaskMonitorService);
spyOn(taskMonitorService, 'queryTaskList').and.returnValue(Observable.of(runningTaskResponse));


---------------------
作者:FlyWine
来源:CSDN
原文:https://blog.csdn.net/wf19930209/article/details/80413904
版权声明:本文为作者原创文章,转载请附上博文链接!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

按键精灵如何调用Excel及按键精灵写入Excel数据的方法教程---入门自动操作表格...

首先来建立一个新的Excel文档&#xff0c;在桌面上点击右键&#xff0c;选择【新建】-【Excel工作表】&#xff0c;命名为【新手学员】。 现在这个新Excel文档是空白的&#xff0c;我们接下来会通过按键精灵的脚本来打开并写入一些数据。打开按键精灵软件&#xff0c;点击【新建…

Maui的学习之路 -- 开篇

Maui的学习之路 -- 开篇想了很久我决定发一个Maui介绍做为开篇&#xff0c;虽然这是老生常谈的话题&#xff0c;但是不能没有这样的探讨&#xff08;请容我水一篇&#xff09;。什么是.NET Maui.NET Maui是微软的一款基于.Net多平台应用 UI (.NET MAUI)的跨平台框架&#xff0c…

互联网架构的演变,看了好多这个讲的确实清楚!

从过去的 OA、CRM、ERP 等单机即可满足要求的系统到现代互联网时代各大公司的分布式、微服务平台&#xff0c;互联网架构正在经历着巨大的变革&#xff0c;技术也在不断的更新迭代。 图片来自 Pexels这也意味着众多软件开发者们的压力和挑战正在不断的加大&#xff0c;这种新技…

【Inpho精品教程】Inpho简介、安装教程(附Inpho8安装包下载)

《无人机航空摄影测量精品教程》合集目录(Pix4d、CC、EPS、PhotoScan、Inpho) Inpho UASMaster是Trimble公司旗下的一款摄影测量处理软件。本文讲解Inpho简介、安装教程(附Inpho8安装包下载)。 文章目录 1. Inpho简介2. Inpho安装教程3. Inpho8下载地址1. Inpho简介 1.1 主…

MAUI使用Masa blazor组件库

上一篇(点击阅读)我们实现了UI在Web端(Blazor Server/Wasm)和客户端(Windows/macOS/Android/iOS)共享&#xff0c;这篇我加上 Masa Blazor[2]组件库的引用&#xff0c;并把前几个月写的时间戳转换[3]工具加上。1. 前置知识关于Masa Blazor请点击Masa Blazor官网[4]了解&#xf…

常用的NSLOG日志打印格式

2019独角兽企业重金招聘Python工程师标准>>> 常用的NSLOG日志打印格式 以下是常用的LOG打印&#xff0c;如果有特殊&#xff0c;请我 &#xff0c;这边会不定期更新。 % 对象 %d, %i 整数 %u 无符整形 %f 浮点/双字 %x, %X 二进制整数 %…

Android studio提示Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.

1、 问题 在Android studio里面运行java的lib库&#xff0c;但是错误提示如下 Build file F:\sourceZip\app\build.gradle line: 2An exception occurred applying plugin request [id: com.android.application] > Failed to apply plugin com.android.internal.applicat…

[转]浅析DDD(领域驱动设计)

最近在做一些微服务相关的设计&#xff0c;内容包括服务的划分&#xff0c;Restful API的设计等。其中比较棘手的就是Service的职责划分&#xff1a;如何抽象具有统一业务范畴的Model&#xff0c;使其模块化&#xff0c;又如何高度提炼并组合多模块&#xff0c;使得业务可独立服…

windows环境实现批量加密文件,并创建加密文件同名(不带后缀)的文件夹,然后把加密文件和图片和文本放入这个文件夹。

1、 需求 我想把资源文件先加密成压缩文件&#xff0c;然后同时创建每个加密压缩文件同名的文件夹&#xff0c;同时需要把这个加密文件拷贝到这个同名的文件夹&#xff0c;然后还需要把一个图片和一个文本文档同时放进这个文件夹&#xff0c;然后在不加密压缩这个文件夹&#…

.NET7之MiniAPI(特别篇) :Preview5优化了JWT验证(上)

在.NET7的Preview5中&#xff0c;优化了asp.net core中的JWT验证&#xff0c;不用像以前繁琐了&#xff0c;更重要的是带来了一组生成Token的工具&#xff0c;可以让开发人员或测试人员不需登录获取Token&#xff0c;而达到测试的目的。创建项目现在来看一下怎么使用&#xff0…

iOS - UTI

一、UTI概念 1、什么是UTI Uniform Type Identifier&#xff0c;是字符串&#xff0c;格式标识符。 根据UTI&#xff0c;可得到相应的其他类型的格式标识符。比如public.jpeg对应于&#xff1a; A four-character file type code (an OSType) of JPEGA filename extension of .…

[转].NET 开源项目 Polly 介绍

今天介绍一个 .NET 开源库&#xff1a;Polly&#xff0c;它是支持 .NET Core 的&#xff0c;目前在 GitHub 的 Star 数量已经接近 5 千&#xff0c;它是一个强大且实用的 .NET 库。 Polly 介绍 官方对 Polly 的介绍是这样的&#xff1a; Polly is a .NET resilience and tran…

「 刘一哥GIS」CSDN专业技术博文专栏目录索引

刘一哥GIS 个人简介&#xff1a;刘一哥&#xff0c;多年研究地图学、地理信息系统、遥感、摄影测量和GPS等应用&#xff0c;精通ArcGIS等软件的应用&#xff0c;精通多门编程语言&#xff0c;擅长GIS二次开发和数据库系统开发&#xff0c;具有丰富的行业经验&#xff0c;致力于…

数据库备份需要注意的

2019独角兽企业重金招聘Python工程师标准>>> 1、PHPMYADMIN无法导出大数据表的 如果你的数据库中有上百个数据表&#xff0c;并且有很多数据表记录都超过了1G&#xff0c;还有很多INNODB数据表&#xff0c;这个时候用PHPMYADMIN导出&#xff0c;你就会发现恢复后可能…

在 .NET 6 中使用 dotnet format 格式化代码

我不得不承认&#xff0c;在 code review 的时候&#xff0c;我花费了很多时间来研究 C# 的代码格式问题&#xff0c;这是没有太大意义的工作&#xff0c;我应该专注于其他事情&#xff0c;而不是观察同事是否忘记格式化代码&#xff0c;或者是使用了其他不同规则的代码编辑器。…

dotnet-exec 0.4.0 released

dotnet-exec 0.4.0 releasedIntrodotnet-exec 是一个 C# 程序的命令行小工具&#xff0c;可以用来运行一些简单的 C# 程序而无需创建项目文件&#xff0c;而且可以自定义项目的入口方法&#xff0c;支持但不限于 Main 方法Install/Updatedotnet-exec 是一个 dotnet tool&#x…

.NET in China - What's New in .NET

点击蓝字关注我们编辑&#xff1a;Alan Wang排版&#xff1a;Rani Sun活动介绍去年11月&#xff0c;.NET 6 的发布&#xff0c;为我们带来了 .NET 多平台应用 UI&#xff08;.NET MAUI&#xff09;。就在前不久&#xff0c;.NET MAUI 已正式发布。未来&#xff0c;作为 .NET 7 …

[转].NET 开源项目 Anet 介绍

使用 Anet 有一段时间了&#xff0c;已经在我的个人网站&#xff08;如 bookist.cc&#xff09;投入使用&#xff0c;目前没有发现什么大问题&#xff0c;所以才敢写篇文章向大家介绍。 GitHub 地址&#xff1a; https://github.com/anet-team/anet Anet 是一个 .NET Core 通用…

强烈推荐国内几款优秀的开源电商系统

一、背景需求 我玩得好的朋友刚创业&#xff0c;搞电商最一块&#xff0c;想做个全套的电商框架系统&#xff0c;希望支持公众号、小程序、H5、pc后台管理等功能&#xff0c;创业初期资金非常紧张&#xff0c;请开发人员做成本太高&#xff0c;然后就咨询我&#xff0c;有没有最…

Maui的学习之路(二)--设置

Maui的学习之路&#xff08;二&#xff09; -- 设置上一篇我们做了Maui的基本介绍&#xff0c;理论上这一篇应该会创建第一个Maui的应用&#xff0c;以便对此进行详细的评估&#xff0c;并逐步深入。如果你需要进行Maui首个应用的创建&#xff0c;那么欢迎访问.NET MAUI 创建移…