[轉]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.pswp.cn/news/285687.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/285687.shtml
英文地址,請注明出處:http://en.pswp.cn/news/285687.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

windows執行python文件提示SyntaxError: Non-ASCII character ‘\xe9‘ in file 1.py on line 7, but no encoding d

1、問題 windows環境下執行python文件&#xff0c;提示錯誤如下 SyntaxError: Non-ASCII character \xe9 in file 1.py on line 7, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details 2、解決辦法 py文件頭部加上 #codingutf-8

按鍵精靈如何調用Excel及按鍵精靈寫入Excel數據的方法教程---入門自動操作表格...

首先來建立一個新的Excel文檔&#xff0c;在桌面上點擊右鍵&#xff0c;選擇【新建】-【Excel工作表】&#xff0c;命名為【新手學員】。 現在這個新Excel文檔是空白的&#xff0c;我們接下來會通過按鍵精靈的腳本來打開并寫入一些數據。打開按鍵精靈軟件&#xff0c;點擊【新建…

第九十五節,移動流體布局和響應式布局總結

移動流體布局和響應式布局總結 寬度與高度 區塊寬度一般用max-width 最大寬度和百分比&#xff0c;來定義寬度&#xff0c;因為要實現自動縮放 高度如果要自適應&#xff0c;就不需要定義高度&#xff0c;或者定義最小高度 注意&#xff1a;橫向的盡量用百分比&#xff0c;如邊…

Maui的學習之路 -- 開篇

Maui的學習之路 -- 開篇想了很久我決定發一個Maui介紹做為開篇&#xff0c;雖然這是老生常談的話題&#xff0c;但是不能沒有這樣的探討&#xff08;請容我水一篇&#xff09;。什么是.NET Maui.NET Maui是微軟的一款基于.Net多平臺應用 UI (.NET MAUI)的跨平臺框架&#xff0c…

【PhotoScan精品教程】任務二:刺像控點,平差,質量報告精度檢查,像控點POS權重調配

《無人機航空攝影測量精品教程》合集目錄(Pix4d、CC、EPS、PhotoScan、Inpho) 【PhotoScan精品教程】任務一:新建工程、導入照片、設置坐標系、導入控制點(POS)、自由空三 【PhotoScan精品教程】任務二:刺像控點,平差,質量報告精度檢查,像控點POS權重調配 【PhotoScan…

互聯網架構的演變,看了好多這個講的確實清楚!

從過去的 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;使得業務可獨立服…

【PhotoScan精品教程】任務三:生成密集點云、生成網格、生成DEM、生成DOM

《無人機航空攝影測量精品教程》合集目錄(Pix4d、CC、EPS、PhotoScan、Inpho) 【PhotoScan精品教程】任務一:新建工程、導入照片、設置坐標系、導入控制點(POS)、自由空三 【PhotoScan精品教程】任務二:刺像控點,平差,質量報告精度檢查,像控點POS權重調配 【PhotoScan…

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 .…

【Inpho精品教程】任務一:Inpho預處理準備(Pix4d生成未畸變圖像、Pix4d生成相機參數文件)

《無人機航空攝影測量精品教程》合集目錄(Pix4d、CC、EPS、PhotoScan、Inpho) Inpho軟對于初學者來說,可能不像pix4d、PhotoScan等那么得心應手,處理過程也稍微有些復雜。Inpho處理的是POS分離的照片,在新建工程之前,需要獲取相機參數、未畸變圖像等,而這些前期的工作一…

學習MongoDB(三) Add an Arbiter to Replica Set 集群中加入仲裁節點

Add an Arbiter to Replica Set 在集群中加入仲裁節點&#xff0c;當集群中主節點掛掉后負責選出新的主節點&#xff0c;仲裁節點也是一個mongo實力&#xff0c;但是它不存儲數據。 1、仲裁節點消耗很小的資源&#xff0c;而且不需要專用的服務器。 2、不能把仲裁節點安裝到集…

[轉]new Thread的弊端及Java四種線程池的使用

介紹new Thread的弊端及Java四種線程池的使用&#xff0c;對Android同樣適用。本文是基礎篇&#xff0c;后面會分享下線程池一些高級功能。 1、new Thread的弊端 執行一個異步任務你還只是如下new Thread嗎&#xff1f; new Thread(new Runnable() {Overridepublic void run() …

【Inpho精品教程】任務二:Inpho創建工程(創建項目、新建相機參數、導入照片、導入POS、生成航條、保存項目)

《無人機航空攝影測量精品教程》合集目錄(Pix4d、CC、EPS、PhotoScan、Inpho) 在任務一中,我們做了Inpho創建工程前的數據預處理工作,獲取了未畸變的圖像、相機參數、Inpho支持的POS等數據,本文在此基礎之上,創建Inpho工程,主要步驟有:創建項目、新建相機參數、導入照片…

return view詳解

1、return View(); 返回值 類型&#xff1a;System.Web.Mvc.ViewResult將視圖呈現給響應的 View() 結果。 注釋 View() 類的此方法重載將返回一個具有空 ViewName 屬性的 ViewResult 對象。 如果你正在編寫控制器操作的單元測試&#xff0c;則需為那些不采用字符串視圖名稱的單…