在Angular测试中使用spy的教程详解

目录

简介

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

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

先决条件

要完成本教程,您需要:

  • 在本地安装 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();
}

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

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测试中使用spy的教程详解的文章就介绍到这了,更多相关Angular中使用spy内容请搜索代码部落以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码部落!

本文章来源于网络,作者是:白如意i,由代码部落进行采编,如涉及侵权请联系删除!转载请注明出处:https://daimabuluo.cc/JavaScript/2307.html

联系我们

在线咨询:点击这里给我发消息

邮件:dick@daimabuluo.cc

遇到问题?请给我们留言

请填写您的邮箱地址,我们将回复您的电子邮件