如何在 Angular 测试中使用 spy

简介

Jasmine spy 用于跟踪或存根函数或方法。spy 是一种检查函数是否被调用或提供自定义返回值的方法。我们可以使用spy 来测试依赖于服务的组件,并避免实际调用服务的方法来获取值。这有助于保持我们的单元测试专注于测试组件本身的内部而不是其依赖关系。

在本文中,您将学习如何在 Angular 项目中使用 Jasmine spy。

先决条件

要完成本教程,您需要:

  • 在本地安装 Node.js,您可以按照《如何安装 Node.js 并创建本地开发环境》进行操作。
  • 一些关于设置 Angular 项目的基础知识。

本教程已使用 Node v16.2.0、npm v7.15.1 和 @angular/core v12.0.4 进行验证。

第 1 步 — 设置项目

让我们使用一个与我们在 Angular 单元测试介绍中使用的示例非常相似的示例。

首先,使用 @angular/cli 创建一个新项目:

ng new angular-test-spies-example

然后,切换到新创建的项目目录:

cd angular-test-spies-example

以前,该应用程序使用两个按钮在 0 到 15 之间增加和减少值。

对于本教程,逻辑将被移动到一个服务中。这将允许多个组件访问相同的中央值。

ng generate service increment-decrement

然后,打开您的代码编辑器中的 increment-decrement.service.ts 并用以下代码替换内容:

import { Injectable } from '@angular/core';@Injectable({providedIn: 'root'
})
export class IncrementDecrementService {value = 0;message!: string;increment() {if (this.value < 15) {this.value += 1;this.message = '';} else {this.message = 'Maximum reached!';}}decrement() {if (this.value > 0) {this.value -= 1;this.message = '';} else {this.message = 'Minimum reached!';}}
}

打开您的代码编辑器中的 app.component.ts 并用以下代码替换内容:

import { Component } from '@angular/core';
import { IncrementDecrementService } from './increment-decrement.service';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent {constructor(public incrementDecrement: IncrementDecrementService) { }increment() {this.incrementDecrement.increment();}decrement() {this.incrementDecrement.decrement();}
}

打开您的代码编辑器中的 app.component.html 并用以下代码替换内容:

<h1>{{ incrementDecrement.value }}</h1><hr><button (click)="increment()" class="increment">Increment</button><button (click)="decrement()" class="decrement">Decrement</button><p class="message">{{ incrementDecrement.message }}
</p>

接下来,打开您的代码编辑器中的 app.component.spec.ts 并修改以下代码行:

import { TestBed, waitForAsync, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';import { AppComponent } from './app.component';
import { IncrementDecrementService } from './increment-decrement.service';describe('AppComponent', () => {let fixture: ComponentFixture<AppComponent>;let debugElement: DebugElement;let incrementDecrementService: IncrementDecrementService;beforeEach(waitForAsync(() => {TestBed.configureTestingModule({declarations: [AppComponent],providers: [ IncrementDecrementService ]}).compileComponents();fixture = TestBed.createComponent(AppComponent);debugElement = fixture.debugElement;incrementDecrementService = debugElement.injector.get(IncrementDecrementService);}));it('should increment in template', () => {debugElement.query(By.css('button.increment')).triggerEventHandler('click', null);fixture.detectChanges();const value = debugElement.query(By.css('h1')).nativeElement.innerText;expect(value).toEqual('1');});it('should stop at 15 and show maximum message', () => {incrementDecrementService.value = 15;debugElement.query(By.css('button.increment')).triggerEventHandler('click', null);fixture.detectChanges();const value = debugElement.query(By.css('h1')).nativeElement.innerText;const message = debugElement.query(By.css('p.message')).nativeElement.innerText;expect(value).toEqual('15');expect(message).toContain('Maximum');});
});

请注意,我们可以使用 debugElement.injector.get 获取对注入服务的引用。

以这种方式测试我们的组件是有效的,但实际调用也将被传递到服务,并且我们的组件没有被孤立测试。接下来,我们将探讨如何使用 spy 来检查方法是否已被调用或提供存根返回值。

步骤 2 —— 监视服务的方法

以下是如何使用 Jasmine 的 spyOn 函数调用一个服务方法并测试它是否被调用:

import { TestBed, waitForAsync, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';import { AppComponent } from './app.component';
import { IncrementDecrementService } from './increment-decrement.service';describe('AppComponent', () => {let fixture: ComponentFixture<AppComponent>;let debugElement: DebugElement;let incrementDecrementService: IncrementDecrementService;let incrementSpy: any;beforeEach(waitForAsync(() => {TestBed.configureTestingModule({declarations: [AppComponent],providers: [ IncrementDecrementService ]}).compileComponents();fixture = TestBed.createComponent(AppComponent);debugElement = fixture.debugElement;incrementDecrementService = debugElement.injector.get(IncrementDecrementService);incrementSpy = spyOn(incrementDecrementService, 'increment').and.callThrough();}));it('should call increment on the service', () => {debugElement.query(By.css('button.increment')).triggerEventHandler('click', null);expect(incrementDecrementService.value).toBe(1);expect(incrementSpy).toHaveBeenCalled();});
});

spyOn 接受两个参数:类实例(在本例中是我们的服务实例)和一个字符串值,表示要监视的方法或函数的名称。

在这里,我们还在 spy 上链接了 .and.callThrough(),这样实际方法仍然会被调用。在这种情况下,我们的 spy 只用于判断方法是否被调用以及监视参数。

以下是断言方法被调用两次的示例:

expect(incrementSpy).toHaveBeenCalledTimes(2);

以下是断言方法未被使用 'error' 参数调用的示例:

expect(incrementSpy).not.toHaveBeenCalledWith('error');

如果我们想避免实际调用服务上的方法,可以在 spy 上使用 .and.returnValue

我们的示例方法不适合这样做,因为它们不返回任何内容,而是改变内部属性。

让我们向服务添加一个实际返回值的新方法:

minimumOrMaximumReached() {return !!(this.message && this.message.length);
}

我们还向组件添加一个新方法,模板将使用它来获取值:

limitReached() {return this.incrementDecrement.minimumOrMaximumReached();
}

现在,如果达到限制,我们的模板将显示一条消息:

<p class="message" *ngIf="limitReached()">Limit reached!
</p>

然后,我们可以测试当达到限制时,我们的模板消息是否会显示,而无需实际调用服务上的方法:

import { TestBed, waitForAsync, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';import { AppComponent } from './app.component';
import { IncrementDecrementService } from './increment-decrement.service';describe('AppComponent', () => {let fixture: ComponentFixture<AppComponent>;let debugElement: DebugElement;let incrementDecrementService: IncrementDecrementService;let minimumOrMaximumSpy: any;beforeEach(waitForAsync(() => {TestBed.configureTestingModule({declarations: [AppComponent],providers: [ IncrementDecrementService ]}).compileComponents();fixture = TestBed.createComponent(AppComponent);debugElement = fixture.debugElement;incrementDecrementService = debugElement.injector.get(IncrementDecrementService);minimumOrMaximumSpy = spyOn(incrementDecrementService, 'minimumOrMaximumReached').and.returnValue(true);}));it(`should show 'Limit reached' message`, () => {fixture.detectChanges();const message = debugElement.query(By.css('p.message')).nativeElement.innerText;expect(message).toEqual('Limit reached!');});
});

结论

在本文中,您学习了如何在 Angular 项目中使用 Jasmine spy。

如果您想了解更多关于 Angular 的知识,请查看我们的 Angular 主题页面,了解练习和编程项目。

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

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

相关文章

空调压缩机补充润滑油的方法

空调压缩机补充润滑油的方法有三种&#xff0c;从吸气截止阀旁边通孔吸入&#xff0c;从加油孔中加入&#xff0c;从曲轴箱下部加入&#xff0c;具体操作步骤如下&#xff1a; 1关闭吸气截止阀&#xff0c;启动压缩机几分钟&#xff0c;将曲轴箱中制冷剂排入冷凝器&#xff0c…

vue2结合electron开发桌面端应用

一、Electron是什么&#xff1f; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 。允许您保持一个 JavaScript 代码代码库并创建可在Windows、macOS和Linux上运行的跨平台应用 。 Electron 经常与 Ch…

scrapy 中间件

就是发送请求的时候&#xff0c;会经过&#xff0c;中间件。中间件会处理&#xff0c;你的请求 下面是代码&#xff1a; # Define here the models for your spider middleware # # See documentation in: # https://docs.scrapy.org/en/latest/topics/spider-middleware.html…

【快速上手ProtoBuf】基本使用

文章目录 1 :peach:初识 ProtoBuf:peach:1.1 :apple:序列化概念:apple:1.2 :apple:ProtoBuf 是什么:apple:1.3 :apple:ProtoBuf 的使用特点:apple: 2 :peach:创建 .proto ⽂件:peach:3 :peach:编译 .proto 文件:peach:3 :peach:序列化与反序列化的使用:peach: 1 &#x1f351;初…

洛谷 2036.PERKET

采用递归法的方式进行题解。 思路&#xff1a;首先我们知道在n种材料当中&#xff0c;我们需要从中选择至少有一种得配料才行。也就是说&#xff0c;我们选择的配料数目是自己决定的&#xff0c;而不是那种组合型得对于你有要求的组合型递归方式。 所以我们会想到用指数型得递…

(五)网络优化与超参数选择--九五小庞

网络容量 网络中神经单元数越多&#xff0c;层数越多&#xff0c;神经网路的拟合能力越强。但是训练速度&#xff0c;难度越大&#xff0c;越容易产生过拟合。 如何选择超参数 所谓超参数&#xff0c;也就是搭建神经网路中&#xff0c;需要我们自己去选择&#xff08;不是通…

LeetCode 刷题 [C++] 第45题.跳跃游戏 II

题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i]i j < n 返回到达 nums[n …

递归函数(c++题解)

题目描述 对于一个递归函数w(a, b, c)。 如果a < 0 or b < 0 or c < 0就返回值1。 如果a > 20 or b > 20 or c > 20就返回W(20,20,20)。 如果a < b并且b < c 就返回w(a, b, c − 1) w(a, b − 1, c − 1) − w(a, b − 1, c)&#xff0c; 其它别…

计算机网络知多少-第1篇

一、 从输入URL到页面展示到底发生了什么&#xff1f; 1. 首先浏览器会查看电脑本地缓存&#xff0c;如果有直接返回&#xff0c;否则需要进行下一步的网络请求。 2. 在网络请求之前&#xff0c;需要先进行DNS解析&#xff0c;来找到请求域名的ip地址。如果是HTTPS请求&#…

【C语言】熟悉文件基础知识

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 文件 为了数据持久化保存&#xff0c;使用文件&#xff0c;否则数据存储在内存中&#xff0c;程序退出&#xff0c;内存回收&#xff0c;数据就会丢失。 程序设计中&…

微信小程序,h5端自适应登陆方式

微信小程序端只显示登陆(获取opid),h5端显示通过账户密码登陆 例如: 通过下面的变量控制: const isWeixin ref(false); // #ifdef MP-WEIXIN isWeixin.value true; // #endif

Git 查看提交历史

命令说明git log查看历史提交记录git blame (file)以列表形式查看指定文件的历史修改记录 git log 在使用 Git 提交了若干更新之后&#xff0c;又或者克隆了某个项目&#xff0c;想回顾下提交历史&#xff0c;我们可以使用 git log 命令查看。 git log 命令用于查看 Git 仓库中…

LIN基础:从LIN Frame开始

目录&#xff1a; 1、LIN的网络拓扑 2、LIN Frame 1&#xff09;Header 2&#xff09;Response 3、LIN的通信规则 1&#xff09;LIN的发送行为示例 2&#xff09;LIN的接收行为示例 虽然LIN总线的通信速率不高&#xff0c;工程中&#xff0c;最高的速率也就19200bps。…

c语言extern关键字

extern 是C和C中的关键字&#xff0c;用于声明一个变量或函数的存在&#xff0c;但不进行定义。 它通常用于在一个源文件中引用另一个源文件中定义的变量或函数。 例如&#xff0c;extern int x; 表示 x 是一个整数变量&#xff0c;但它的实际定义将在其他文件中。在引用它的文…

StarRocks——Stream Load 事务接口实现原理

目录 前言 一、StarRocks 数据导入 二、StarRocks 事务写入原理 三、InLong 实时写入StarRocks原理 3.1 InLong概述 3.2 基本原理 3.3 详细流程 3.3.1 任务写入数据 3.3.2 任务保存检查点 3.3.3 任务如何确认保存点成功 3.3.4 任务如何初始化 3.4 Exactly Once 保证…

Leetcode - 周赛386

目录 一&#xff0c;3046. 分割数组 二&#xff0c;3047. 求交集区域内的最大正方形面积 三&#xff0c;3048. 标记所有下标的最早秒数 I 四&#xff0c;3049. 标记所有下标的最早秒数 II 一&#xff0c;3046. 分割数组 将题目给的数组nums分成两个数组&#xff0c;且这两个…

探索RedisJSON:将JSON数据力量带入Redis世界

探索RedisJSON&#xff1a;将JSON数据力量带入Redis世界 当我们谈论数据存储和查询时&#xff0c;Redis和JSON都是无法忽视的重要角色。Redis以其高效的键值存储、快速的读/写速度、以及丰富的数据结构赢得了开发者的喜爱。而JSON&#xff0c;作为一种轻量级的数据交换格式&am…

「Vue3系列」Vue3 条件语句/循环语句

文章目录 一、Vue3 条件语句1. v-if2. v-else-if 和 v-else3. v-show4. 使用计算属性进行条件渲染5. v-if与v-show比较v-ifv-show性能考虑使用场景 二、Vue3 循环语句1. 遍历数组2. 遍历对象3. 使用索引4. 注意事项 三、相关链接 一、Vue3 条件语句 在 Vue 3 中&#xff0c;你…

盲人出行:科技创造美好的未来

在繁忙的都市中&#xff0c;我每天都要面对许多挑战&#xff0c;盲人出行安全保障一直难以得到落实。我看不见这个世界&#xff0c;只能依靠触觉和听觉来感知周围的一切。然而&#xff0c;我从未放弃过对生活的热爱和对未来的憧憬。在一次机缘巧合下&#xff0c;我认识了一款名…

C3_W2_Collaborative_RecSys_Assignment_吴恩达_中英_Pytorch

Practice lab: Collaborative Filtering Recommender Systems(实践实验室:协同过滤推荐系统) In this exercise, you will implement collaborative filtering to build a recommender system for movies. 在本次实验中&#xff0c;你将实现协同过滤来构建一个电影推荐系统。 …